summaryrefslogtreecommitdiff
path: root/src/radeon_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/radeon_driver.c')
-rw-r--r--src/radeon_driver.c5944
1 files changed, 5944 insertions, 0 deletions
diff --git a/src/radeon_driver.c b/src/radeon_driver.c
new file mode 100644
index 0000000..ae58433
--- /dev/null
+++ b/src/radeon_driver.c
@@ -0,0 +1,5944 @@
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_driver.c,v 1.91 2003/02/25 03:50:15 dawes 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 <martin@xfree86.org>
+ * Rickard E. Faith <faith@valinux.com>
+ * Alan Hourihane <alanh@fairlite.demon.co.uk>
+ *
+ * Credits:
+ *
+ * Thanks to Ani Joshi <ajoshi@shell.unixbox.com> 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.
+ */
+
+ /* Driver data structures */
+#include "radeon.h"
+#include "radeon_macros.h"
+#include "radeon_probe.h"
+#include "radeon_reg.h"
+#include "radeon_version.h"
+
+#ifdef XF86DRI
+#define _XF86DRI_SERVER_
+#include "radeon_dri.h"
+#include "radeon_sarea.h"
+#endif
+
+#define USE_FB /* not until overlays */
+#ifdef USE_FB
+#include "fb.h"
+#else
+
+ /* CFB support */
+#define PSZ 8
+#include "cfb.h"
+#undef PSZ
+#include "cfb16.h"
+#include "cfb24.h"
+#include "cfb32.h"
+#endif
+
+ /* 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"
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#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);
+
+typedef enum {
+ OPTION_NOACCEL,
+ OPTION_SW_CURSOR,
+ OPTION_DAC_6BIT,
+ OPTION_DAC_8BIT,
+#ifdef XF86DRI
+ OPTION_IS_PCI,
+ OPTION_CP_PIO,
+ OPTION_USEC_TIMEOUT,
+ OPTION_AGP_MODE,
+ OPTION_AGP_FW,
+ OPTION_AGP_SIZE,
+ OPTION_RING_SIZE,
+ OPTION_BUFFER_SIZE,
+ OPTION_DEPTH_MOVE,
+ OPTION_PAGE_FLIP,
+ OPTION_NO_BACKBUFFER,
+#endif
+ OPTION_PANEL_OFF,
+ OPTION_DDC_MODE,
+ OPTION_CLONE_DISPLAY,
+ OPTION_CLONE_MODE,
+ OPTION_CLONE_HSYNC,
+ OPTION_CLONE_VREFRESH,
+ OPTION_FBDEV,
+ OPTION_VIDEO_KEY
+} RADEONOpts;
+
+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_IS_PCI, "ForcePCIMode", OPTV_BOOLEAN, {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_AGP_SIZE, "AGPSize", 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_CLONE_DISPLAY, "CloneDisplay", OPTV_INTEGER, {0}, FALSE },
+ { OPTION_CLONE_MODE, "CloneMode", OPTV_ANYSTR, {0}, FALSE },
+ { OPTION_CLONE_HSYNC, "CloneHSync", OPTV_ANYSTR, {0}, FALSE },
+ { OPTION_CLONE_VREFRESH, "CloneVRefresh", OPTV_ANYSTR, {0}, FALSE },
+ { OPTION_FBDEV, "UseFBDev", OPTV_BOOLEAN, {0}, FALSE },
+ { OPTION_VIDEO_KEY, "VideoKey", OPTV_INTEGER, {0}, FALSE },
+ { -1, NULL, OPTV_NONE, {0}, FALSE }
+};
+
+RADEONRAMRec RADEONRAM[] = { /* Memory Specifications
+ From Radeon Manual */
+ { 4, 4, 1, 2, 1, 2, 1, 16, 12, "64-bit SDR SDRAM" },
+ { 4, 4, 3, 3, 2, 3, 1, 16, 12, "64-bit DDR SDRAM" },
+};
+
+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",
+ "fbdevHWValidMode",
+
+ "fbdevHWMapMMIO",
+ "fbdevHWMapVidmem",
+ "fbdevHWUnmapMMIO",
+ "fbdevHWUnmapVidmem",
+
+ NULL
+};
+
+static const char *ddcSymbols[] = {
+ "xf86PrintEDID",
+ "xf86DoEDID_DDC1",
+ "xf86DoEDID_DDC2",
+ NULL
+};
+
+#ifdef USE_FB
+static const char *fbSymbols[] = {
+ "fbScreenInit",
+ "fbPictureInit",
+ NULL
+};
+#else
+static const char *cfbSymbols[] = {
+ "cfbScreenInit",
+ "cfb16ScreenInit",
+ "cfb24ScreenInit",
+ "cfb32ScreenInit",
+ NULL
+};
+#endif
+
+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",
+ NULL
+};
+
+static const char *driShadowFBSymbols[] = {
+ "ShadowFBInit",
+ NULL
+};
+#endif
+
+static const char *vbeSymbols[] = {
+ "VBEInit",
+ "vbeDoEDID",
+ NULL
+};
+
+static const char *int10Symbols[] = {
+ "xf86InitInt10",
+ "xf86FreeInt10",
+ "xf86int10Addr",
+ 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,
+#ifdef USE_FB
+ fbSymbols,
+#else
+ cfbSymbols,
+#endif
+ 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},
+};
+
+extern int gRADEONEntityIndex;
+
+struct RADEONInt10Save {
+ CARD32 MEM_CNTL;
+ CARD32 MEMSIZE;
+ CARD32 MPP_TB_CONFIG;
+};
+
+static Bool RADEONMapMMIO(ScrnInfoPtr pScrn);
+static Bool RADEONUnmapMMIO(ScrnInfoPtr pScrn);
+
+static void
+RADEONPreInt10Save(ScrnInfoPtr pScrn, void **pPtr)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO;
+ int mapped = 0;
+ CARD32 CardTmp;
+ static struct RADEONInt10Save SaveStruct = { 0, 0, 0 };
+
+ /*
+ * First make sure we have the pci and mmio info and that mmio is mapped
+ */
+ if (!info->PciInfo)
+ info->PciInfo = xf86GetPciInfoForEntity(info->pEnt->index);
+ if (!info->PciTag)
+ info->PciTag = pciTag(info->PciInfo->bus, info->PciInfo->device,
+ info->PciInfo->func);
+ if (!info->MMIOAddr)
+ info->MMIOAddr = info->PciInfo->memBase[2] & 0xffffff00;
+ if (!info->MMIO) {
+ RADEONMapMMIO(pScrn);
+ mapped = 1;
+ }
+ RADEONMMIO = info->MMIO;
+
+ /* 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;
+
+ /* Unmap mmio space if we mapped it */
+ if (mapped)
+ RADEONUnmapMMIO(pScrn);
+}
+
+static void
+RADEONPostInt10Check(ScrnInfoPtr pScrn, void *ptr)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO;
+ struct RADEONInt10Save *pSave = ptr;
+ CARD32 CardTmp;
+ int mapped = 0;
+
+ /* If we don't have a valid (non-zero) saved MEM_CNTL, get out now */
+ if (!pSave || !pSave->MEM_CNTL)
+ return;
+
+ /* First make sure that mmio is mapped */
+ if (!info->MMIO) {
+ RADEONMapMMIO(pScrn);
+ mapped = 1;
+ }
+ RADEONMMIO = info->MMIO;
+
+ /*
+ * 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 (%08x), setting to %08x\n",
+ CardTmp, 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 (%08x), setting to %08x\n",
+ CardTmp, 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> (%02x), setting to %02x\n",
+ CardTmp >> 24, pSave->MPP_TB_CONFIG >> 24);
+ CardTmp &= 0x00ffffffu;
+ CardTmp |= (pSave->MPP_TB_CONFIG & 0xff000000u);
+ OUTREG(RADEON_MPP_TB_CONFIG, CardTmp);
+ }
+
+ /* Unmap mmio space if we mapped it */
+ if (mapped)
+ RADEONUnmapMMIO(pScrn);
+}
+
+/* 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)
+{
+ 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_VSYNC_DIS |
+ RADEON_CRTC_HSYNC_DIS,
+ ~(RADEON_CRTC_DISPLAY_DIS |
+ RADEON_CRTC_VSYNC_DIS |
+ RADEON_CRTC_HSYNC_DIS));
+ break;
+
+ case MT_NONE:
+ default:
+ break;
+ }
+ if (info->Clone)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ RADEON_CRTC2_DISP_DIS |
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_DIS,
+ ~(RADEON_CRTC2_DISP_DIS |
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_DIS));
+ } else {
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ RADEON_CRTC2_DISP_DIS |
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_DIS,
+ ~(RADEON_CRTC2_DISP_DIS |
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_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 |
+ RADEON_CRTC_VSYNC_DIS |
+ RADEON_CRTC_HSYNC_DIS));
+ break;
+
+ case MT_NONE:
+ default:
+ break;
+ }
+ if (info->Clone)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ 0,
+ ~(RADEON_CRTC2_DISP_DIS |
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_DIS));
+ } else {
+ switch (info->DisplayType) {
+ case MT_LCD:
+ case MT_DFP:
+ case MT_CRT:
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ 0,
+ ~(RADEON_CRTC2_DISP_DIS |
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_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;
+}
+
+/* Read the Video BIOS block and the FP registers (if applicable) */
+static Bool RADEONGetBIOSParameters(ScrnInfoPtr pScrn, xf86Int10InfoPtr pInt10)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned long tmp, i;
+ unsigned char *RADEONMMIO;
+ Bool BypassSecondary = FALSE;
+ int CloneDispOption;
+
+#define RADEON_BIOS8(v) (info->VBIOS[v])
+#define RADEON_BIOS16(v) (info->VBIOS[v] | \
+ (info->VBIOS[(v) + 1] << 8))
+#define RADEON_BIOS32(v) (info->VBIOS[v] | \
+ (info->VBIOS[(v) + 1] << 8) | \
+ (info->VBIOS[(v) + 2] << 16) | \
+ (info->VBIOS[(v) + 3] << 24))
+
+ if (!(info->VBIOS = xalloc(RADEON_VBIOS_SIZE))) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Cannot allocate space for hold Video BIOS!\n");
+ return FALSE;
+ }
+
+ if (pInt10) {
+ info->BIOSAddr = pInt10->BIOSseg << 4;
+ (void)memcpy(info->VBIOS, xf86int10Addr(pInt10, info->BIOSAddr),
+ RADEON_VBIOS_SIZE);
+ } else {
+ xf86ReadPciBIOS(0, info->PciTag, 0, info->VBIOS, RADEON_VBIOS_SIZE);
+ if (info->VBIOS[0] != 0x55 || info->VBIOS[1] != 0xaa) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Video BIOS not detected in PCI space!\n");
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Attempting to read Video BIOS from "
+ "legacy ISA space!\n");
+ info->BIOSAddr = 0x000c0000;
+ xf86ReadDomainMemory(info->PciTag, info->BIOSAddr,
+ RADEON_VBIOS_SIZE, info->VBIOS);
+ }
+ }
+
+ if (info->VBIOS[0] != 0x55 || info->VBIOS[1] != 0xaa) {
+ xfree(info->VBIOS);
+ info->VBIOS = NULL;
+ info->BIOSAddr = 0x00000000;
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Video BIOS not found!\n");
+ return TRUE;
+ }
+
+ info->FPBIOSstart = RADEON_BIOS16(0x48);
+ info->OverlayOnCRTC2 = FALSE;
+
+ RADEONMapMMIO(pScrn);
+ RADEONMMIO = info->MMIO;
+
+ /* FIXME: using BIOS scratch registers to detect connected monitors
+ * may not be a reliable way.... should use EDID data. Also it only
+ * works with for VE/M6, no such registers in regular RADEON!!!
+ */
+
+ /* VE and M6 have both DVI and CRT ports (for M6 DVI port can be
+ * switch to DFP port). The DVI port can also be conneted to a CRT
+ * with an adapter. Here is the definition of ports for this
+ * driver:
+ *
+ * (1) If both port are connected, DVI port will be treated as the
+ * Primary port (first screen in XF86Config, uses CRTC1) and CRT
+ * port will be treated as the Secondary port (second screen in
+ * XF86Config, uses CRTC2)
+ *
+ * (2) If only one screen specified in XF86Config, it will be used
+ * for DVI port if a monitor is connected to DVI port, otherwise
+ * (only one monitor is connected the CRT port) it will be used for
+ * CRT port.
+ */
+
+ if (info->HasCRTC2) {
+ /* FIXME: this may not be reliable */
+ tmp = INREG(RADEON_BIOS_4_SCRATCH);
+
+ if (info->IsSecondary) {
+ /* Check Port2 (CRT port) -- for the existing boards (VE &
+ * M6), this port can only be connected to a CRT
+ */
+ if (tmp & 0x02) info->DisplayType = MT_CRT;
+ else if (tmp & 0x800) info->DisplayType = MT_DFP;
+ else if (tmp & 0x400) info->DisplayType = MT_LCD;
+ else if (tmp & 0x1000) info->DisplayType = MT_CTV;
+ else if (tmp & 0x2000) info->DisplayType = MT_STV;
+ else info->DisplayType = MT_CRT;
+
+ } else {
+ info->Clone = FALSE;
+ info->CloneType = MT_NONE;
+
+ /* Check Primary (DVI/DFP port) */
+ if (tmp & 0x08) info->DisplayType = MT_DFP;
+ else if (tmp & 0x04) info->DisplayType = MT_LCD;
+ else if (tmp & 0x0200) info->DisplayType = MT_CRT;
+ else if (tmp & 0x10) info->DisplayType = MT_CTV;
+ else if (tmp & 0x20) info->DisplayType = MT_STV;
+ else info->DisplayType = MT_NONE;
+
+ if (info->DisplayType == MT_NONE) {
+ /* DVI port has no monitor connected, try CRT port.
+ * If something on CRT port, treat it as primary
+ */
+ if (xf86IsEntityShared(pScrn->entityList[0])) {
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+ pRADEONEnt->BypassSecondary = TRUE;
+ }
+
+ if (tmp & 0x02) info->DisplayType = MT_CRT;
+ else if (tmp & 0x800) info->DisplayType = MT_DFP;
+ else if (tmp & 0x400) info->DisplayType = MT_LCD;
+ else if (tmp & 0x1000) info->DisplayType = MT_CTV;
+ else if (tmp & 0x2000) info->DisplayType = MT_STV;
+ else {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "No monitor detected!!!\n");
+ return FALSE;
+ }
+ BypassSecondary = TRUE;
+ } else {
+ if (tmp & 0x02) {
+ info->CloneType = MT_CRT;
+ info->Clone = TRUE;
+ } else if (tmp & 0x800) {
+ info->CloneType = MT_DFP;
+ info->Clone = TRUE;
+ }
+ }
+
+ /* FIXME: This option is too complicated. We need to
+ * find a better way to handle all cases.
+ *
+ * CloneDisplay options:
+ * 0 -- disable
+ * 1 -- auto-detect (default)
+ * 2 -- force on
+ * 3 -- auto-detect + 2nd head overlay.
+ * 4 -- force on + 2nd head overlay.
+ * others -- auto-detect
+ *
+ * Force on: it will force the clone mode on even no display
+ * is detected. With this option together with the proper
+ * CloneHSync and CloneVRefresh options, we can turn on the
+ * CRT ouput on the 2nd head regardless if a monitor is
+ * connected there. This way, we can plug in a CRT to the
+ * second head later after X server has started.
+ *
+ * 2nd head overlay: it will force the hardware overlay on
+ * CRTC2 (used by 2nd head). Since we only have one overlay,
+ * we have to decide which head to use it (the overlay space
+ * on the other head will be blank). 2nd head overlay is on
+ * automatically when PanelOff option is effective.
+ */
+ if (xf86GetOptValInteger(info->Options, OPTION_CLONE_DISPLAY,
+ &(CloneDispOption))) {
+ char *s = NULL;
+
+ if (CloneDispOption < 0 || CloneDispOption > 4) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Illegal CloneDisplay Option set, "
+ "using default\n");
+ CloneDispOption = 1;
+ }
+
+ switch (CloneDispOption) {
+ case 0: s = "Disable"; break;
+ case 1: s = "Auto-detect"; break;
+ case 2: s = "Force On"; break;
+ case 3: s = "Auto-detect -- use 2nd head overlay"; break;
+ case 4: s = "Force On -- use 2nd head overlay"; break;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "CloneDisplay option: %s (%d)\n",
+ s, CloneDispOption);
+ } else {
+ /* Default to auto-detect */
+ CloneDispOption = 1;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "CloneDisplay option not set -- "
+ "defaulting to auto-detect\n");
+ }
+
+ if (CloneDispOption == 0) {
+ info->Clone = FALSE;
+ } else if ((CloneDispOption == 2 || CloneDispOption == 4)
+ && !info->Clone) {
+ info->CloneType = MT_CRT;
+ info->Clone = TRUE;
+ }
+
+ /* This will be used to set OV0_SCALAR_CNTL */
+ if (info->Clone && (CloneDispOption == 3 || CloneDispOption == 4))
+ info->OverlayOnCRTC2 = TRUE;
+ }
+ } else {
+ /* Regular Radeon ASIC, only one CRTC, but it could be used for
+ * DFP with a DVI output, like AIW board
+ */
+ tmp = INREG(RADEON_FP_GEN_CNTL);
+ if (tmp & RADEON_FP_EN_TMDS) info->DisplayType = MT_DFP;
+ else info->DisplayType = MT_CRT;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s Display == Type %d\n",
+ (info->IsSecondary ? "Secondary" : "Primary"),
+ info->DisplayType);
+
+ RADEONMMIO = NULL;
+ RADEONUnmapMMIO(pScrn);
+
+ info->HBlank = 0;
+ info->HOverPlus = 0;
+ info->HSyncWidth = 0;
+ info->VBlank = 0;
+ info->VOverPlus = 0;
+ info->VSyncWidth = 0;
+ info->DotClock = 0;
+
+ if (info->DisplayType == MT_LCD) {
+ tmp = RADEON_BIOS16(info->FPBIOSstart + 0x40);
+ if (!tmp) {
+ info->PanelPwrDly = 200;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "No Panel Info Table found in BIOS!\n");
+ } else {
+ char stmp[30];
+ int tmp0;
+
+ for (i = 0; i < 24; i++)
+ stmp[i] = RADEON_BIOS8(tmp+i+1);
+ stmp[24] = 0;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Panel ID string: %s\n", stmp);
+
+ info->PanelXRes = RADEON_BIOS16(tmp+25);
+ info->PanelYRes = RADEON_BIOS16(tmp+27);
+ xf86DrvMsg(0, X_INFO, "Panel Size from BIOS: %dx%d\n",
+ info->PanelXRes, info->PanelYRes);
+
+ info->PanelPwrDly = RADEON_BIOS16(tmp+44);
+ if (info->PanelPwrDly > 2000 || info->PanelPwrDly < 0)
+ info->PanelPwrDly = 2000;
+ for (i = 0; i < 20; i++) {
+ tmp0 = RADEON_BIOS16(tmp+64+i*2);
+ if (tmp0 == 0) break;
+ if ((RADEON_BIOS16(tmp0) == info->PanelXRes) &&
+ (RADEON_BIOS16(tmp0+2) == info->PanelYRes)) {
+ info->HBlank = (RADEON_BIOS16(tmp0+17) -
+ RADEON_BIOS16(tmp0+19)) * 8;
+ info->HOverPlus = (RADEON_BIOS16(tmp0+21) -
+ RADEON_BIOS16(tmp0+19) - 1) * 8;
+ info->HSyncWidth = RADEON_BIOS8(tmp0+23) * 8;
+ info->VBlank = (RADEON_BIOS16(tmp0+24) -
+ RADEON_BIOS16(tmp0+26));
+ info->VOverPlus = ((RADEON_BIOS16(tmp0+28) & 0x7ff) -
+ RADEON_BIOS16(tmp0+26));
+ info->VSyncWidth = ((RADEON_BIOS16(tmp0+28) & 0xf800)
+ >> 11);
+ info->DotClock = RADEON_BIOS16(tmp0+9) * 10;
+ info->Flags = 0;
+ }
+ }
+ }
+ } else if ((info->DisplayType == MT_DFP) && info->HasCRTC2) {
+ tmp = RADEON_BIOS16(info->FPBIOSstart + 0x34);
+ if (tmp != 0) {
+ tmp = RADEON_BIOS16(tmp + 2);
+ if (tmp != 0) {
+ /* 18 bytes of EDID data should be here */
+ info->DotClock = RADEON_BIOS16(tmp) * 10;
+ info->PanelXRes =
+ ((RADEON_BIOS8(tmp + 4) & 0xf0) << 4) +
+ RADEON_BIOS8(tmp + 2);
+ info->HBlank =
+ ((RADEON_BIOS8(tmp + 4) & 0x0f) << 8) +
+ RADEON_BIOS8(tmp + 3);
+ info->PanelYRes =
+ ((RADEON_BIOS8(tmp + 7) & 0xf0) << 4) +
+ RADEON_BIOS8(tmp + 5);
+ info->VBlank =
+ ((RADEON_BIOS8(tmp + 7) & 0x0f) << 8) +
+ RADEON_BIOS8(tmp + 6);
+ info->HOverPlus =
+ ((RADEON_BIOS8(tmp + 11) & 0xc0) << 2) +
+ RADEON_BIOS8(tmp + 8);
+ info->HSyncWidth =
+ ((RADEON_BIOS8(tmp + 11) & 0x30) << 4) +
+ RADEON_BIOS8(tmp + 9);
+ info->VOverPlus =
+ ((RADEON_BIOS8(tmp + 11) & 0x0c) << 2) +
+ ((RADEON_BIOS8(tmp + 10) & 0xf0) >> 4);
+ info->VSyncWidth =
+ ((RADEON_BIOS8(tmp + 11) & 0x03) << 4) +
+ (RADEON_BIOS8(tmp + 10) & 0x0f);
+ info->Flags = 0;
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "No DFP timing table detected\n");
+ }
+ }
+
+ RADEONTRACE(("DFP Info: ----------------------\n"
+ "pixel clock: %d KHz\n"
+ "panel size: %dx%d\n"
+ "H. Blanking: %d\n"
+ "H. Sync. Offset: %d\n"
+ "H. Sync. Width: %d\n"
+ "V. Blanking: %d\n"
+ "V. Sync. Offset: %d\n"
+ "V. Sync. Width: %d\n",
+ info->DotClock,
+ info->PanelXRes, info->PanelYRes,
+ info->HBlank,
+ info->HOverPlus,
+ info->HSyncWidth,
+ info->VBlank, info->VOverPlus, info->VSyncWidth));
+ }
+
+ /* Detect connector type from BIOS, used for I2C/DDC qeurying EDID,
+ * Only available for VE or newer cards */
+
+ /* DELL OEM card doesn't seem to follow the conviention for BIOS's
+ * DDC type, we have to make a special case. Following hard coded
+ * type works with both CRT+CRT and DVI+DVI cases
+ */
+ if (info->IsDell && info->DellType == 2) {
+ if (info->IsSecondary)
+ info->DDCType = DDC_CRT2;
+ else
+ info->DDCType = DDC_DVI;
+ info->CloneDDCType = DDC_CRT2;
+ } else if ((tmp = RADEON_BIOS16(info->FPBIOSstart + 0x50))) {
+ for (i = 1; i < 4; i++) {
+ unsigned int tmp0;
+ if (!RADEON_BIOS8(tmp + i*2) && i > 1) break;
+
+ /* Note: Secondary port (CRT port) actually uses primary DAC */
+ tmp0 = RADEON_BIOS16(tmp + i*2);
+ if (tmp0 & 0x01) {
+ if (!info->IsSecondary && !BypassSecondary)
+ info->DDCType = (tmp0 & 0x0f00) >> 8;
+ } else { /* Primary DAC */
+ if (info->Clone)
+ info->CloneDDCType = (tmp0 & 0x0f00) >> 8;
+ else if (info->IsSecondary ||
+ BypassSecondary ||
+ !info->HasCRTC2) {
+ info->DDCType = (tmp0 & 0x0f00) >> 8;
+ }
+ }
+ }
+ } else {
+ /* Orignal radeon cards, set it to DDC_VGA, this will not work
+ * with AIW, it should be DDC_DVI, let it fall back to VBE calls
+ * for AIW
+ */
+ info->DDCType = DDC_VGA;
+ }
+
+ return TRUE;
+}
+
+/* Read PLL parameters from BIOS block. Default to typical values if
+ * there is no BIOS.
+ */
+static Bool RADEONGetPLLParameters(ScrnInfoPtr pScrn)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ RADEONPLLPtr pll = &info->pll;
+ CARD16 bios_header;
+ CARD16 pll_info_block;
+
+ if (!info->VBIOS) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Video BIOS not detected, using default PLL parameters!\n");
+ /* These probably aren't going to work for
+ the card you are using. Specifically,
+ reference freq can be 29.50MHz,
+ 28.63MHz, or 14.32MHz. YMMV. */
+
+ /* These are somewhat sane defaults for Mac boards, we will need
+ * to find a good way of getting these from OpenFirmware
+ */
+ pll->reference_freq = 2700;
+ pll->reference_div = 67;
+ pll->min_pll_freq = 12500;
+ pll->max_pll_freq = 35000;
+ pll->xclk = 16615;
+ } else {
+ bios_header = RADEON_BIOS16(0x48);
+ pll_info_block = RADEON_BIOS16(bios_header + 0x30);
+ RADEONTRACE(("Header at 0x%04x; PLL Information at 0x%04x\n",
+ bios_header, pll_info_block));
+
+ pll->reference_freq = RADEON_BIOS16(pll_info_block + 0x0e);
+ pll->reference_div = RADEON_BIOS16(pll_info_block + 0x10);
+ pll->min_pll_freq = RADEON_BIOS32(pll_info_block + 0x12);
+ pll->max_pll_freq = RADEON_BIOS32(pll_info_block + 0x16);
+ pll->xclk = RADEON_BIOS16(pll_info_block + 0x08);
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "PLL parameters: rf=%d rd=%d min=%d max=%d; xclk=%d\n",
+ pll->reference_freq,
+ pll->reference_div,
+ pll->min_pll_freq,
+ pll->max_pll_freq,
+ pll->xclk);
+
+ 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, 8, 8, 8, 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;
+}
+
+/* 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;
+ int offset = 0; /* RAM Type */
+ MessageType from;
+ unsigned char *RADEONMMIO;
+
+ /* 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;
+ switch (info->Chipset) {
+ case PCI_CHIP_RADEON_LY:
+ case PCI_CHIP_RADEON_LZ:
+ info->ChipFamily = CHIP_FAMILY_M6;
+ break;
+
+ case PCI_CHIP_RV100_QY:
+ case PCI_CHIP_RV100_QZ:
+ info->ChipFamily = CHIP_FAMILY_VE;
+ break;
+
+ case PCI_CHIP_R200_BB:
+ case PCI_CHIP_R200_QH:
+ case PCI_CHIP_R200_QI:
+ case PCI_CHIP_R200_QJ:
+ case PCI_CHIP_R200_QK:
+ case PCI_CHIP_R200_QL:
+ case PCI_CHIP_R200_QM:
+ case PCI_CHIP_R200_QN:
+ case PCI_CHIP_R200_QO:
+ case PCI_CHIP_R200_Qh:
+ case PCI_CHIP_R200_Qi:
+ case PCI_CHIP_R200_Qj:
+ case PCI_CHIP_R200_Qk:
+ case PCI_CHIP_R200_Ql:
+ info->ChipFamily = CHIP_FAMILY_R200;
+ break;
+
+ case PCI_CHIP_RV200_QW: /* RV200 desktop */
+ case PCI_CHIP_RV200_QX:
+ info->ChipFamily = CHIP_FAMILY_RV200;
+ break;
+
+ case PCI_CHIP_RADEON_LW:
+ case PCI_CHIP_RADEON_LX:
+ info->ChipFamily = CHIP_FAMILY_M7;
+ break;
+
+ case PCI_CHIP_RV250_Id:
+ case PCI_CHIP_RV250_Ie:
+ case PCI_CHIP_RV250_If:
+ case PCI_CHIP_RV250_Ig:
+ info->ChipFamily = CHIP_FAMILY_RV250;
+ break;
+
+ case PCI_CHIP_RV250_Ld:
+ case PCI_CHIP_RV250_Le:
+ case PCI_CHIP_RV250_Lf:
+ case PCI_CHIP_RV250_Lg:
+ info->ChipFamily = CHIP_FAMILY_M9;
+ 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;
+
+ default:
+ /* Original Radeon/7200 */
+ info->ChipFamily = CHIP_FAMILY_RADEON;
+ info->HasCRTC2 = FALSE;
+ }
+
+ /* Here is the special case for DELL's VE card.
+ * It needs some special handlings for it's 2nd head to work.
+ */
+ info->IsDell = FALSE;
+ if (info->ChipFamily == CHIP_FAMILY_VE &&
+ info->PciInfo->subsysVendor == PCI_VENDOR_ATI &&
+ info->PciInfo->subsysCard & (1 << 12)) { /* DELL's signature */
+ if (info->PciInfo->subsysCard & 0xb00) {
+ info->IsDell = TRUE;
+ info->DellType = 2; /* DVI+DVI config, this seems to be the
+ * only known type for now, can be
+ * connected to both DVI+DVI and VGA+VGA
+ * dongles.
+ */
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "DELL OEM Card detected with %s (type %d)\n",
+ (info->DellType == 2) ? "DVI+DVI / VGA+VGA" : "VGA+VGA",
+ info->DellType);
+ } else {
+ info->DellType = 0; /* Unknown */
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Unknown type of DELL's Card (SSCID %x), "
+ "treated as normal type\n",
+ info->PciInfo->subsysCard);
+ }
+ }
+
+ /* Framebuffer */
+
+ from = X_PROBED;
+ info->LinearAddr = info->PciInfo->memBase[0] & 0xfc000000;
+ pScrn->memPhysBase = info->LinearAddr;
+ if (dev->MemBase) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Linear address override, using 0x%08x instead of 0x%08x\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);
+
+ /* MMIO registers */
+ from = X_PROBED;
+ info->MMIOAddr = info->PciInfo->memBase[2] & 0xffffff00;
+ if (dev->IOBase) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "MMIO address override, using 0x%08x instead of 0x%08x\n",
+ dev->IOBase,
+ info->MMIOAddr);
+ info->MMIOAddr = dev->IOBase;
+ from = X_CONFIG;
+ } else if (!info->MMIOAddr) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid MMIO address\n");
+ return FALSE;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, from,
+ "MMIO registers at 0x%08lx\n", info->MMIOAddr);
+
+ /* BIOS */
+ from = X_PROBED;
+ info->BIOSAddr = info->PciInfo->biosBase & 0xfffe0000;
+ if (dev->BiosBase) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "BIOS address override, using 0x%08x instead of 0x%08x\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);
+ }
+
+ RADEONMapMMIO(pScrn);
+ RADEONMMIO = info->MMIO;
+
+ /* Read registers used to determine options */
+ from = X_PROBED;
+ if (info->FBDev)
+ pScrn->videoRam = fbdevHWGetVidmem(pScrn) / 1024;
+ else
+ pScrn->videoRam = INREG(RADEON_CONFIG_MEMSIZE) / 1024;
+
+ /* 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. */
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+ RADEONInfoPtr info1;
+
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0], gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+ pScrn->videoRam /= 2;
+ pRADEONEnt->pPrimaryScrn->videoRam = pScrn->videoRam;
+
+ info1 = RADEONPTR(pRADEONEnt->pPrimaryScrn);
+ info1->FbMapSize = pScrn->videoRam * 1024;
+ info->LinearAddr += pScrn->videoRam * 1024;
+ info1->Clone = FALSE;
+ info1->CurCloneMode = NULL;
+ }
+
+ 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);
+ RADEONMMIO = NULL;
+ RADEONUnmapMMIO(pScrn);
+
+ /* RAM */
+ switch (info->MemCntl >> 30) {
+ case 0: offset = 0; break; /* 64-bit SDR SDRAM */
+ case 1: offset = 1; break; /* 64-bit DDR SDRAM */
+ default: offset = 0;
+ }
+ info->ram = &RADEONRAM[offset];
+
+ 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 (%s)\n", pScrn->videoRam, info->ram->name);
+
+#ifdef XF86DRI
+ /* AGP/PCI */
+ if (xf86ReturnOptValBool(info->Options, OPTION_IS_PCI, FALSE)) {
+ info->IsPCI = TRUE;
+ xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forced into PCI-only mode\n");
+ } else {
+ switch (info->Chipset) {
+#if 0
+ case PCI_CHIP_RADEON_XX: info->IsPCI = TRUE; break;
+#endif
+ case PCI_CHIP_RV100_QY:
+ case PCI_CHIP_RV100_QZ:
+ case PCI_CHIP_RADEON_LW:
+ case PCI_CHIP_RADEON_LX:
+ case PCI_CHIP_RADEON_LY:
+ case PCI_CHIP_RADEON_LZ:
+ case PCI_CHIP_RADEON_QD:
+ case PCI_CHIP_RADEON_QE:
+ case PCI_CHIP_RADEON_QF:
+ case PCI_CHIP_RADEON_QG:
+ case PCI_CHIP_R200_BB:
+ case PCI_CHIP_R200_QH:
+ case PCI_CHIP_R200_QI:
+ case PCI_CHIP_R200_QJ:
+ case PCI_CHIP_R200_QK:
+ case PCI_CHIP_R200_QL:
+ case PCI_CHIP_R200_QM:
+ case PCI_CHIP_R200_QN:
+ case PCI_CHIP_R200_QO:
+ case PCI_CHIP_R200_Qh:
+ case PCI_CHIP_R200_Qi:
+ case PCI_CHIP_R200_Qj:
+ case PCI_CHIP_R200_Qk:
+ case PCI_CHIP_R200_Ql:
+ case PCI_CHIP_RV200_QW:
+ case PCI_CHIP_RV200_QX:
+ case PCI_CHIP_RV250_Id:
+ case PCI_CHIP_RV250_Ie:
+ case PCI_CHIP_RV250_If:
+ case PCI_CHIP_RV250_Ig:
+ case PCI_CHIP_RV250_Ld:
+ case PCI_CHIP_RV250_Le:
+ case PCI_CHIP_RV250_Lf:
+ case PCI_CHIP_RV250_Lg:
+ 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:
+ default: info->IsPCI = FALSE; break;
+ }
+ }
+#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);
+}
+
+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;
+
+ switch (info->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:
+ return FALSE;
+ }
+
+ 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;
+ }
+}
+
+static xf86MonPtr RADEONDoDDC(ScrnInfoPtr pScrn, xf86Int10InfoPtr pInt10)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ xf86MonPtr MonInfo = NULL;
+ unsigned char *RADEONMMIO;
+ int i;
+
+ /* We'll use DDC2, BIOS EDID can only detect the monitor connected
+ * to one port. For VE, BIOS EDID detects the monitor connected to
+ * DVI port by default. If no monitor their, it will try CRT port
+ */
+
+ /* Read and output monitor info using DDC2 over I2C bus */
+ if (info->pI2CBus && info->ddc2) {
+ int j;
+
+ if (!RADEONMapMMIO(pScrn)) return NULL;
+ RADEONMMIO = info->MMIO;
+ 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 < 50; 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;
+ }
+
+ RADEONUnmapMMIO(pScrn);
+ }
+
+ if (!MonInfo && pInt10 && (info->DDCReg == RADEON_GPIO_VGA_DDC)) {
+ if (xf86LoadSubModule(pScrn, "vbe")) {
+ vbeInfoPtr pVbe;
+ pVbe = VBEInit(pInt10, info->pEnt->index);
+ if (pVbe) {
+ for (i = 0; i < 5; i++) {
+ MonInfo = vbeDoEDID(pVbe, NULL);
+ info->ddc_bios = TRUE;
+ if (MonInfo)
+ break;
+ }
+ } else
+ info->ddc_bios = FALSE;
+ }
+ }
+
+ if (MonInfo) {
+ if (info->ddc2)
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "I2C EDID Info:\n");
+ else if (info->ddc_bios)
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "BIOS EDID Info:\n");
+ else return NULL;
+
+ xf86PrintEDID(MonInfo);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "End of DDC Monitor info\n\n");
+
+ xf86SetDDCproperties(pScrn, MonInfo);
+ return MonInfo;
+ }
+ else return NULL;
+}
+
+/* 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) {
+
+ 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 pScrn, char **ppModeName,
+ RADEONMonitorType DisplayType)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ DisplayModePtr p;
+ DisplayModePtr last = NULL;
+ DisplayModePtr first = NULL;
+ DisplayModePtr ddcModes = NULL;
+ int count = 0;
+ int i, width, height;
+
+ pScrn->virtualX = pScrn->display->virtualX;
+ pScrn->virtualY = pScrn->display->virtualY;
+
+ if (pScrn->monitor->DDC) {
+ int maxVirtX = pScrn->virtualX;
+ int maxVirtY = pScrn->virtualY;
+
+ if (DisplayType != MT_CRT) {
+ /* 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) {
+ /* 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;
+ }
+
+ 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 mdoe\n");
+ }
+
+ return new;
+}
+
+/* XFree86's xf86ValidateModes routine doesn't work well with DFPs, so
+ * here is our own validation routine.
+ */
+static int RADEONValidateFPModes(ScrnInfoPtr pScrn, char **ppModeName)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ DisplayModePtr last = NULL;
+ DisplayModePtr new = NULL;
+ DisplayModePtr first = NULL;
+ 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++) {
+ /* FIXME: Use HDisplay and VDisplay instead of mode string */
+ 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;
+ }
+
+ /* 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 RADEONValidateCloneModes(ScrnInfoPtr pScrn)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ ClockRangePtr clockRanges;
+ DisplayModePtr tmp_mode = NULL;
+ DisplayModePtr clone_mode, save_mode;
+ int modesFound = 0;
+ int count = 0;
+ int tmp_hdisplay = 0;
+ int tmp_vdisplay = 0;
+ int i, save_n_hsync, save_n_vrefresh;
+ range save_hsync, save_vrefresh;
+ char *s;
+ char **clone_mode_names = NULL;
+ Bool ddc_mode = info->ddc_mode;
+
+ /* Save all infomations that will be changed by clone mode validateion */
+ save_mode = pScrn->modes;
+ pScrn->modes = NULL;
+
+ /* Clone display mode names, duplicate all mode names for primary
+ * head. Allocate one more, in case pScrn->display->modes[0] ==
+ * NULL */
+ while (pScrn->display->modes[count]) count++;
+ clone_mode_names = xnfalloc((count+2) * sizeof(char*));
+ for (i = 0; i < count; i++) {
+ clone_mode_names[i] = xnfalloc(strlen(pScrn->display->modes[i]) + 1);
+ strcpy(clone_mode_names[i], pScrn->display->modes[i]);
+ }
+ clone_mode_names[count] = NULL;
+ clone_mode_names[count+1] = NULL;
+
+ 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 = FALSE;
+ clockRanges->doubleScanAllowed = FALSE;
+
+ /* Only take one clone mode from config file for now, rest of clone
+ * modes will copy from primary head.
+ */
+ if ((s = xf86GetOptValString(info->Options, OPTION_CLONE_MODE))) {
+ if (sscanf(s, "%dx%d", &tmp_hdisplay, &tmp_vdisplay) == 2) {
+ if(count > 0) free(clone_mode_names[0]);
+ else count++;
+ clone_mode_names[0] = xnfalloc(strlen(s)+1);
+ sprintf(clone_mode_names[0], "%dx%d", tmp_hdisplay, tmp_vdisplay);
+ xf86DrvMsg(0, X_INFO, "Clone mode %s in config file is used\n");
+ }
+ }
+
+ if (pScrn->display->virtualX < tmp_hdisplay)
+ pScrn->display->virtualX = tmp_hdisplay;
+ if (pScrn->display->virtualY < tmp_vdisplay)
+ pScrn->display->virtualY = tmp_vdisplay;
+
+ save_hsync = pScrn->monitor->hsync[0];
+ save_vrefresh = pScrn->monitor->vrefresh[0];
+ save_n_hsync = pScrn->monitor->nHsync;
+ save_n_vrefresh = pScrn->monitor->nVrefresh;
+
+ pScrn->monitor->DDC = NULL;
+ pScrn->monitor->nHsync = 0;
+ pScrn->monitor->nVrefresh = 0;
+
+ if ((s = xf86GetOptValString(info->Options, OPTION_CLONE_HSYNC))) {
+ if (sscanf(s, "%f-%f", &pScrn->monitor->hsync[0].lo,
+ &pScrn->monitor->hsync[0].hi) == 2) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "HSync for CloneMode from config file: %s\n", s);
+ pScrn->monitor->nHsync = 1;
+ } else {
+ pScrn->monitor->nHsync = 0;
+ }
+ }
+
+ if ((s = xf86GetOptValString(info->Options, OPTION_CLONE_VREFRESH))) {
+ if (sscanf(s, "%f-%f", &pScrn->monitor->vrefresh[0].lo,
+ &pScrn->monitor->vrefresh[0].hi) == 2) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "VRefresh for CloneMode from config file: %s\n", s);
+ pScrn->monitor->nVrefresh = 1;
+ } else {
+ pScrn->monitor->nVrefresh = 0;
+ }
+ }
+
+ if ((pScrn->monitor->nVrefresh == 0) || (pScrn->monitor->nHsync == 0) ||
+ (info->CloneType != MT_CRT) || info->ddc_mode) {
+ unsigned int save_ddc_reg;
+ save_ddc_reg = info->DDCReg;
+ switch (info->CloneDDCType) {
+ 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 = 0; break;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "DDC detection (type %d) for clone modes\n",
+ info->CloneDDCType);
+
+ /* When primary head has an invalid DDC type, I2C is not
+ * initialized, so we do it here.
+ */
+ if (!info->ddc2) info->ddc2 = xf86I2CBusInit(info->pI2CBus);
+
+ pScrn->monitor->DDC = RADEONDoDDC(pScrn, NULL);
+ if (pScrn->monitor->DDC) {
+ if (info->CloneType == MT_CRT) {
+ if (pScrn->monitor->nHsync == 0)
+ RADEONSetSyncRangeFromEdid(pScrn, 1);
+ if (pScrn->monitor->nVrefresh == 0)
+ RADEONSetSyncRangeFromEdid(pScrn, 0);
+ }
+ } else if (info->ddc_mode) {
+ ddc_mode = FALSE;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "No DDC data available for clone mode, "
+ "DDCMode option is dismissed\n");
+ }
+ info->DDCReg = save_ddc_reg;
+ }
+
+ if (info->CloneType == MT_CRT && !ddc_mode) {
+ modesFound =
+ xf86ValidateModes(pScrn, pScrn->monitor->Modes,
+ clone_mode_names,
+ 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 {
+ /* Try to add DDC modes */
+ info->IsSecondary = TRUE; /* Fake it */
+ modesFound = RADEONValidateDDCModes(pScrn, clone_mode_names,
+ info->CloneType);
+ info->IsSecondary = FALSE; /* Restore it!!! */
+
+ /* If that fails and we're connect to a flat panel, then try to
+ * add the flat panel modes
+ */
+ if (modesFound < 1 && info->DisplayType != MT_CRT)
+ modesFound = RADEONValidateFPModes(pScrn, clone_mode_names);
+ }
+
+ if (modesFound > 0) {
+ xf86SetCrtcForModes(pScrn, 0);
+ xf86PrintModes(pScrn);
+ for (i = 0; i < modesFound; i++) {
+ while (pScrn->modes->status != MODE_OK) {
+ pScrn->modes = pScrn->modes->next;
+ }
+ if (!pScrn->modes) break;
+
+ clone_mode = xnfcalloc (1, sizeof (DisplayModeRec));
+ if (!clone_mode || !pScrn->modes) break;
+ memcpy(clone_mode, pScrn->modes, sizeof(DisplayModeRec));
+ clone_mode->name = xnfalloc(strlen(pScrn->modes->name) + 1);
+ strcpy(clone_mode->name, pScrn->modes->name);
+
+ if (i == 0) {
+ info->CloneModes = clone_mode;
+ info->CurCloneMode = clone_mode;
+ } else {
+ clone_mode->prev = tmp_mode;
+ clone_mode->prev->next = clone_mode;
+ }
+
+ tmp_mode = clone_mode;
+ clone_mode->next = NULL;
+ pScrn->modes = pScrn->modes->next;
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Valid Clone Mode: %s\n", clone_mode->name);
+ }
+ }
+
+ /* Clone_mode_names list is no longer needed, free it. */
+ if (clone_mode_names) {
+ for (i = 0; clone_mode_names[i]; i++) {
+ free(clone_mode_names[i]);
+ clone_mode_names[i] = NULL;
+ }
+
+ free(clone_mode_names);
+ clone_mode_names = NULL;
+ }
+
+ /* We need to restore all changed info for the primary head */
+ pScrn->modes = save_mode;
+
+ pScrn->monitor->hsync[0] = save_hsync;
+ pScrn->monitor->vrefresh[0] = save_vrefresh;
+ pScrn->monitor->nHsync = save_n_hsync;
+ pScrn->monitor->nVrefresh = save_n_vrefresh;
+
+ /*
+ * Also delete the clockRanges (if it was setup) since it will be
+ * set up during the primary head initialization.
+ */
+ while (pScrn->clockRanges) {
+ ClockRangesPtr CRtmp = pScrn->clockRanges;
+ pScrn->clockRanges = pScrn->clockRanges->next;
+ xfree(CRtmp);
+ }
+
+ /* modePool is no longer needed, free it */
+ while (pScrn->modePool)
+ xf86DeleteMode(&pScrn->modePool, pScrn->modePool);
+ pScrn->modePool = NULL;
+
+ 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;
+ char *mod = NULL;
+#ifndef USE_FB
+ const char *Sym = NULL;
+#endif
+
+ /* 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.
+ */
+ info->ddc_mode =
+ xf86ReturnOptValBool(info->Options, OPTION_DDC_MODE, FALSE);
+
+ /* Here is a hack for cloning first display on the second head. If
+ * we don't do this, when both heads are connected, the same CRTC
+ * will be used to drive them according to the capability of the
+ * primary head. This can cause an unstable or blank screen, or
+ * even worse it can damage a monitor. This feature is also
+ * important for laptops (using M6, M7), where the panel can't be
+ * disconnect when one wants to use the CRT port. Although 2
+ * Screens can be set up in the config file for displaying same
+ * content on two monitors, it has problems with cursor, overlay,
+ * DRI.
+ */
+ if (info->HasCRTC2) {
+ if (info->Clone) {
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+
+ /* 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,
+ "Clone modes validation ------------ \n");
+
+ modesFound = RADEONValidateCloneModes(pScrn);
+ if (modesFound < 1) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "No valid mode found for CRTC2 clone\n");
+ info->Clone = FALSE;
+ info->CurCloneMode = NULL;
+ }
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Total of %d clone modes found ------------ \n\n",
+ modesFound);
+ }
+ }
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Validating modes on %s head (DDCType: %d) ---------\n",
+ info->IsSecondary ? "Secondary" : "Primary",
+ info->DDCType);
+
+ pScrn->monitor->DDC = RADEONDoDDC(pScrn, pInt10);
+ 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 (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);
+ }
+
+ 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);
+
+ /* If that fails and we're connect to a flat panel, then try to
+ * add the flat panel modes
+ */
+ if (modesFound < 1 && info->DisplayType != MT_CRT)
+ modesFound = RADEONValidateFPModes(pScrn, pScrn->display->modes);
+
+ /* 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;
+ }
+
+ /* Setup the screen's clockRanges for the VidMode extension */
+ pScrn->clockRanges = xnfcalloc(sizeof(*(pScrn->clockRanges)), 1);
+ memcpy(pScrn->clockRanges, clockRanges, sizeof(*clockRanges));
+ pScrn->clockRanges->strategy = LOOKUP_BEST_REFRESH;
+ }
+
+ xf86SetCrtcForModes(pScrn, 0);
+
+ /* We need to adjust virtual size if the clone modes have larger
+ * display size.
+ */
+ if (info->Clone && info->CloneModes) {
+ DisplayModePtr clone_mode = info->CloneModes;
+ while (1) {
+ if ((clone_mode->HDisplay > pScrn->virtualX) ||
+ (clone_mode->VDisplay > pScrn->virtualY)) {
+ pScrn->virtualX =
+ pScrn->display->virtualX = clone_mode->HDisplay;
+ pScrn->virtualY =
+ pScrn->display->virtualY = clone_mode->VDisplay;
+ RADEONSetPitch(pScrn);
+ }
+ if (!clone_mode->next) break;
+ clone_mode = clone_mode->next;
+ }
+ }
+
+ pScrn->currentMode = pScrn->modes;
+ xf86PrintModes(pScrn);
+
+ /* Set DPI */
+ xf86SetDpi(pScrn, 0, 0);
+
+ /* Get ScreenInit function */
+#ifdef USE_FB
+ mod = "fb";
+#else
+ switch (pScrn->bitsPerPixel) {
+ case 8: mod = "cfb"; Sym = "cfbScreenInit"; break;
+ case 16: mod = "cfb16"; Sym = "cfb16ScreenInit"; break;
+ case 32: mod = "cfb32"; Sym = "cfb32ScreenInit"; break;
+ }
+#endif
+
+ if (mod && !xf86LoadSubModule(pScrn, mod)) return FALSE;
+
+#ifdef USE_FB
+ xf86LoaderReqSymLists(fbSymbols, NULL);
+#else
+ xf86LoaderReqSymbols(Sym, NULL);
+#endif
+
+ 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 = 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->agpSize = RADEON_DEFAULT_AGP_SIZE;
+ info->ringSize = RADEON_DEFAULT_RING_SIZE;
+ info->bufSize = RADEON_DEFAULT_BUFFER_SIZE;
+ info->agpTexSize = RADEON_DEFAULT_AGP_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_AGP_SIZE, (int *)&(info->agpSize))) {
+ switch (info->agpSize) {
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ break;
+
+ default:
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Illegal AGP size: %d MB\n", info->agpSize);
+ return FALSE;
+ }
+ }
+
+ if (xf86GetOptValInteger(info->Options,
+ OPTION_RING_SIZE, &(info->ringSize))) {
+ if (info->ringSize < 1 || info->ringSize >= (int)info->agpSize) {
+ 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->agpSize) {
+ 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->agpTexSize >
+ (int)info->agpSize) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Buffers are too big for requested AGP space\n");
+ return FALSE;
+ }
+
+ info->agpTexSize = info->agpSize - (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;
+
+ RADEONTRACE(("RADEONPreInit\n"));
+ if (pScrn->numEntities != 1) return FALSE;
+
+ if (!RADEONGetRec(pScrn)) return FALSE;
+
+ info = RADEONPTR(pScrn);
+ info->IsSecondary = FALSE;
+ info->Clone = FALSE;
+ info->CurCloneMode = NULL;
+ info->CloneModes = NULL;
+ info->IsSwitching = FALSE;
+
+ info->pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
+ 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);
+
+#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(pScrn->entityList[0])) {
+ if (xf86IsPrimInitDone(pScrn->entityList[0])) {
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+
+ info->IsSecondary = TRUE;
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+ if (pRADEONEnt->BypassSecondary) {
+ pRADEONEnt->HasSecondary = FALSE;
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Only one monitor detected, Second screen "
+ "will NOT be created\n");
+ return FALSE;
+ }
+ pRADEONEnt->pSecondaryScrn = pScrn;
+ } else {
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+
+ xf86SetPrimInitDone(pScrn->entityList[0]);
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ gRADEONEntityIndex);
+
+ pRADEONEnt = pPriv->ptr;
+ pRADEONEnt->pPrimaryScrn = pScrn;
+ pRADEONEnt->IsDRIEnabled = FALSE;
+ pRADEONEnt->BypassSecondary = FALSE;
+ pRADEONEnt->RestorePrimary = FALSE;
+ pRADEONEnt->IsSecondaryRestored = FALSE;
+ }
+ }
+
+ if (flags & PROBE_DETECT) {
+ RADEONProbeDDC(pScrn, info->pEnt->index);
+ RADEONPostInt10Check(pScrn, int10_save);
+ return TRUE;
+ }
+
+ if (!xf86LoadSubModule(pScrn, "vgahw")) return FALSE;
+ xf86LoaderReqSymLists(vgahwSymbols, NULL);
+ if (!vgaHWGetHWRec(pScrn)) {
+ RADEONFreeRec(pScrn);
+ return FALSE;
+ }
+
+ 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 (!RADEONPreInitWeight(pScrn))
+ goto fail;
+
+ if (xf86GetOptValInteger(info->Options, OPTION_VIDEO_KEY,
+ &(info->videoKey))) {
+ xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "video key set to 0x%x\n",
+ info->videoKey);
+ } else {
+ info->videoKey = 0x1E;
+ }
+
+ if (xf86ReturnOptValBool(info->Options, OPTION_FBDEV, FALSE)) {
+ /* check for Linux framebuffer device */
+
+ if (xf86LoadSubModule(pScrn, "fbdevhw")) {
+ xf86LoaderReqSymLists(fbdevHWSymbols, NULL);
+
+ if (fbdevHWInit(pScrn, info->PciInfo, NULL)) {
+ pScrn->ValidMode = fbdevHWValidMode;
+ info->FBDev = TRUE;
+ xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
+ "Using framebuffer device\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "fbdevHWInit failed, not using framebuffer device\n");
+ }
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Couldn't load fbdevhw module, not using framebuffer device\n");
+ }
+ }
+
+ if (!info->FBDev)
+ if (!RADEONPreInitInt10(pScrn, &pInt10))
+ goto fail;
+
+ RADEONPostInt10Check(pScrn, int10_save);
+
+ if (!RADEONPreInitConfig(pScrn))
+ goto fail;
+
+#if !defined(__powerpc__)
+ if (!RADEONGetBIOSParameters(pScrn, pInt10))
+ goto fail;
+#else
+ /* Force type to CRT since we currently can't read BIOS to tell us
+ * what kind of heads we have.
+ */
+ info->DisplayType = MT_CRT;
+#endif
+
+ RADEONPreInitDDC(pScrn);
+
+ if (!RADEONGetPLLParameters(pScrn)) goto fail;
+
+ if (!RADEONPreInitGamma(pScrn)) goto fail;
+
+ if (!RADEONPreInitModes(pScrn, pInt10)) goto fail;
+
+ if (!RADEONPreInitCursor(pScrn)) goto fail;
+
+ if (!RADEONPreInitAccel(pScrn)) goto fail;
+
+#ifdef XF86DRI
+ if (!RADEONPreInitDRI(pScrn)) goto fail;
+#endif
+
+ /* Free the video bios (if applicable) */
+ if (info->VBIOS) {
+ xfree(info->VBIOS);
+ info->VBIOS = NULL;
+ }
+
+ /* Free int10 info */
+ if (pInt10)
+ xf86FreeInt10(pInt10);
+
+ xf86DrvMsg(pScrn->scrnIndex, X_NOTICE,
+ "For information on using the multimedia capabilities\n of this"
+ " adapter, please see http://gatos.sf.net.\n");
+
+ return TRUE;
+
+fail:
+ /* Pre-init failed. */
+
+ /* Free the video bios (if applicable) */
+ if (info->VBIOS) {
+ xfree(info->VBIOS);
+ info->VBIOS = NULL;
+ }
+
+ /* Free int10 info */
+ if (pInt10)
+ xf86FreeInt10(pInt10);
+
+ vgaHWFreeHWRec(pScrn);
+ RADEONFreeRec(pScrn);
+ return FALSE;
+}
+
+/* Load a palette */
+static void RADEONLoadPalette(ScrnInfoPtr pScrn, int numColors,
+ int *indices, LOCO *colors, VisualPtr pVisual)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+ int i;
+ int idx, j;
+ unsigned char r, g, b;
+
+#ifdef XF86DRI
+ if (info->CPStarted) DRILock(pScrn->pScreen, 0);
+#endif
+
+ if (info->accelOn) info->accel->Sync(pScrn);
+
+ if (info->FBDev) {
+ fbdevHWLoadPalette(pScrn, numColors, indices, colors, pVisual);
+ } else {
+ /* If the second monitor is connected, we also need to deal with
+ * the secondary palette
+ */
+ if (info->IsSecondary) j = 1;
+ else j = 0;
+
+ PAL_SELECT(j);
+
+ if (info->CurrentLayout.depth == 15) {
+ /* 15bpp mode. This sends 32 values. */
+ for (i = 0; i < numColors; i++) {
+ idx = indices[i];
+ r = colors[idx].red;
+ g = colors[idx].green;
+ b = colors[idx].blue;
+ OUTPAL(idx * 8, r, g, b);
+ }
+ } else if (info->CurrentLayout.depth == 16) {
+ /* 16bpp mode. This sends 64 values.
+ *
+ * There are twice as many green values as there are values
+ * for red and blue. So, we take each red and blue pair,
+ * and combine it with each of the two green values.
+ */
+ for (i = 0; i < numColors; i++) {
+ idx = indices[i];
+ r = colors[idx / 2].red;
+ g = colors[idx].green;
+ b = colors[idx / 2].blue;
+ RADEONWaitForFifo(pScrn, 32); /* delay */
+ OUTPAL(idx * 4, r, g, b);
+
+ /* AH - Added to write extra green data - How come this isn't
+ * needed on R128? We didn't load the extra green data in the
+ * other routine
+ */
+ if (idx <= 31) {
+ r = colors[idx].red;
+ g = colors[(idx * 2) + 1].green;
+ b = colors[idx].blue;
+ RADEONWaitForFifo(pScrn, 32); /* delay */
+ OUTPAL(idx * 8, r, g, b);
+ }
+ }
+ } else {
+ /* 8bpp mode. This sends 256 values. */
+ for (i = 0; i < numColors; i++) {
+ idx = indices[i];
+ r = colors[idx].red;
+ b = colors[idx].blue;
+ g = colors[idx].green;
+ RADEONWaitForFifo(pScrn, 32); /* delay */
+ OUTPAL(idx, r, g, b);
+ }
+ }
+
+ if (info->Clone) {
+ PAL_SELECT(1);
+ if (info->CurrentLayout.depth == 15) {
+ /* 15bpp mode. This sends 32 values. */
+ for (i = 0; i < numColors; i++) {
+ idx = indices[i];
+ r = colors[idx].red;
+ g = colors[idx].green;
+ b = colors[idx].blue;
+ OUTPAL(idx * 8, r, g, b);
+ }
+ } else if (info->CurrentLayout.depth == 16) {
+ /* 16bpp mode. This sends 64 values.
+ *
+ * There are twice as many green values as there are values
+ * for red and blue. So, we take each red and blue pair,
+ * and combine it with each of the two green values.
+ */
+ for (i = 0; i < numColors; i++) {
+ idx = indices[i];
+ r = colors[idx / 2].red;
+ g = colors[idx].green;
+ b = colors[idx / 2].blue;
+ OUTPAL(idx * 4, r, g, b);
+
+ /* AH - Added to write extra green data - How come
+ * this isn't needed on R128? We didn't load the
+ * extra green data in the other routine.
+ */
+ if (idx <= 31) {
+ r = colors[idx].red;
+ g = colors[(idx * 2) + 1].green;
+ b = colors[idx].blue;
+ OUTPAL(idx * 8, r, g, b);
+ }
+ }
+ } else {
+ /* 8bpp mode. This sends 256 values. */
+ for (i = 0; i < numColors; i++) {
+ idx = indices[i];
+ r = colors[idx].red;
+ b = colors[idx].blue;
+ g = colors[idx].green;
+ OUTPAL(idx, r, g, b);
+ }
+ }
+ }
+ }
+
+#ifdef XF86DRI
+ if (info->CPStarted) DRIUnlock(pScrn->pScreen);
+#endif
+}
+
+static void RADEONBlockHandler(int i, pointer blockData,
+ pointer pTimeout, pointer pReadmask)
+{
+ ScreenPtr pScreen = screenInfo.screens[i];
+ ScrnInfoPtr pScrn = xf86Screens[i];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+
+#ifdef XF86DRI
+ if (info->directRenderingEnabled)
+ FLUSH_RING();
+#endif
+
+ pScreen->BlockHandler = info->BlockHandler;
+ (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask);
+ pScreen->BlockHandler = RADEONBlockHandler;
+
+ if (info->VideoTimerCallback)
+ (*info->VideoTimerCallback)(pScrn, currentTime.milliseconds);
+}
+
+/* Called at the start of each server generation. */
+Bool RADEONScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ BoxRec MemBox;
+ int y2;
+
+ RADEONTRACE(("RADEONScreenInit %x %d\n",
+ pScrn->memPhysBase, pScrn->fbOffset));
+
+#ifdef XF86DRI
+ /* Turn off the CP for now. */
+ info->CPInUse = FALSE;
+ info->CPStarted = FALSE;
+ info->directRenderingEnabled = FALSE;
+#endif
+ info->accelOn = FALSE;
+ pScrn->fbOffset = 0;
+ if (info->IsSecondary) pScrn->fbOffset = pScrn->videoRam * 1024;
+ if (!RADEONMapMem(pScrn)) return FALSE;
+
+#ifdef XF86DRI
+ info->fbX = 0;
+ info->fbY = 0;
+#endif
+
+ info->PaletteSavedOnVT = FALSE;
+
+ RADEONSave(pScrn);
+ if (info->FBDev) {
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ if (!fbdevHWModeInit(pScrn, pScrn->currentMode)) return FALSE;
+ info->ModeReg.surface_cntl = INREG(RADEON_SURFACE_CNTL);
+ } else {
+ if (!RADEONModeInit(pScrn, pScrn->currentMode)) return FALSE;
+ }
+
+ RADEONSaveScreen(pScreen, SCREEN_SAVER_ON);
+
+ pScrn->AdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
+
+ if (info->CurCloneMode) {
+ info->CloneFrameX0 =
+ (pScrn->virtualX - info->CurCloneMode->HDisplay) / 2;
+ info->CloneFrameY0 =
+ (pScrn->virtualY - info->CurCloneMode->VDisplay) / 2;
+ RADEONDoAdjustFrame(pScrn, info->CloneFrameX0, info->CloneFrameY0, TRUE);
+ }
+
+ /* Visual setup */
+ miClearVisualTypes();
+ if (!miSetVisualTypes(pScrn->depth,
+ miGetDefaultVisualMask(pScrn->depth),
+ pScrn->rgbBits,
+ pScrn->defaultVisual)) return FALSE;
+ miSetPixmapDepths ();
+
+#ifdef XF86DRI
+ /* Setup DRI after visuals have been
+ established, but before cfbScreenInit is
+ called. cfbScreenInit will eventually
+ call the driver's InitGLXVisuals call
+ back. */
+ {
+ /* FIXME: When we move to dynamic allocation of back and depth
+ * buffers, we will want to revisit the following check for 3
+ * times the virtual size of the screen below.
+ */
+ int width_bytes = (pScrn->displayWidth *
+ info->CurrentLayout.pixel_bytes);
+ int maxy = info->FbMapSize / width_bytes;
+
+ if (xf86ReturnOptValBool(info->Options, OPTION_NOACCEL, FALSE)) {
+ xf86DrvMsg(scrnIndex, X_WARNING,
+ "Acceleration disabled, not initializing the DRI\n");
+ info->directRenderingEnabled = FALSE;
+ } else if (maxy <= pScrn->virtualY * 3) {
+ xf86DrvMsg(scrnIndex, X_WARNING,
+ "Static buffer allocation failed -- "
+ "need at least %d kB video memory\n",
+ (pScrn->displayWidth * pScrn->virtualY *
+ info->CurrentLayout.pixel_bytes * 3 + 1023) / 1024);
+ info->directRenderingEnabled = FALSE;
+ } else if (info->ChipFamily >= CHIP_FAMILY_R300) {
+ info->directRenderingEnabled = FALSE;
+ xf86DrvMsg(scrnIndex, X_WARNING,
+ "Direct rendering not yet supported on "
+ "Radeon 9500/9700 and newer cards\n");
+ } else {
+ if (info->IsSecondary)
+ info->directRenderingEnabled = FALSE;
+ else {
+ /* Xinerama has sync problem with DRI, disable it for now */
+ if (xf86IsEntityShared(pScrn->entityList[0])) {
+ info->directRenderingEnabled = FALSE;
+ xf86DrvMsg(scrnIndex, X_WARNING,
+ "Direct Rendering Disabled -- "
+ "Dual-head configuration is not working with "
+ "DRI at present.\n"
+ "Please use only one Device/Screen "
+ "section in your XFConfig file.\n");
+ } else {
+ info->directRenderingEnabled =
+ RADEONDRIScreenInit(pScreen);
+ }
+
+ if (xf86IsEntityShared(pScrn->entityList[0])) {
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+ pRADEONEnt->IsDRIEnabled = info->directRenderingEnabled;
+ }
+ }
+ }
+ }
+#endif
+
+#ifdef USE_FB
+ if (!fbScreenInit(pScreen, info->FB,
+ pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth,
+ pScrn->bitsPerPixel))
+ return FALSE;
+#else
+ switch (pScrn->bitsPerPixel) {
+ case 8:
+ if (!cfbScreenInit(pScreen, info->FB,
+ pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth))
+ return FALSE;
+ break;
+ case 16:
+ if (!cfb16ScreenInit(pScreen, info->FB,
+ pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth))
+ return FALSE;
+ break;
+ case 32:
+ if (!cfb32ScreenInit(pScreen, info->FB,
+ pScrn->virtualX, pScrn->virtualY,
+ pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth))
+ return FALSE;
+ break;
+ default:
+ xf86DrvMsg(scrnIndex, X_ERROR,
+ "Invalid bpp (%d)\n", pScrn->bitsPerPixel);
+ return FALSE;
+ }
+#endif
+
+ xf86SetBlackWhitePixels(pScreen);
+
+ if (pScrn->bitsPerPixel > 8) {
+ VisualPtr visual;
+
+ visual = pScreen->visuals + pScreen->numVisuals;
+ while (--visual >= pScreen->visuals) {
+ if ((visual->class | DynamicClass) == DirectColor) {
+ visual->offsetRed = pScrn->offset.red;
+ visual->offsetGreen = pScrn->offset.green;
+ visual->offsetBlue = pScrn->offset.blue;
+ visual->redMask = pScrn->mask.red;
+ visual->greenMask = pScrn->mask.green;
+ visual->blueMask = pScrn->mask.blue;
+ }
+ }
+ }
+
+#ifdef USE_FB
+ /* Must be after RGB order fixed */
+ fbPictureInit (pScreen, 0, 0);
+#endif
+
+#ifdef RENDER
+ if (PictureGetSubpixelOrder (pScreen) == SubPixelUnknown)
+ {
+ int subPixelOrder;
+
+ switch (info->DisplayType) {
+ case MT_NONE: subPixelOrder = SubPixelUnknown; break;
+ case MT_LCD: subPixelOrder = SubPixelHorizontalRGB; break;
+ case MT_DFP: subPixelOrder = SubPixelHorizontalRGB; break;
+ default: subPixelOrder = SubPixelNone; break;
+ }
+ PictureSetSubpixelOrder (pScreen, subPixelOrder);
+ }
+#endif
+ /* Memory manager setup */
+#ifdef XF86DRI
+ if (info->directRenderingEnabled) {
+ FBAreaPtr fbarea;
+ int width_bytes = (pScrn->displayWidth *
+ info->CurrentLayout.pixel_bytes);
+ int cpp = info->CurrentLayout.pixel_bytes;
+ int bufferSize = ((pScrn->virtualY * width_bytes
+ + RADEON_BUFFER_ALIGN)
+ & ~RADEON_BUFFER_ALIGN);
+ int depthSize = ((((pScrn->virtualY+15) & ~15) * width_bytes
+ + RADEON_BUFFER_ALIGN)
+ & ~RADEON_BUFFER_ALIGN);
+ int l;
+ int scanlines;
+
+ info->frontOffset = 0;
+ info->frontPitch = pScrn->displayWidth;
+
+ switch (info->CPMode) {
+ case RADEON_DEFAULT_CP_PIO_MODE:
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "CP in PIO mode\n");
+ break;
+ case RADEON_DEFAULT_CP_BM_MODE:
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "CP in BM mode\n");
+ break;
+ default:
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "CP in UNKNOWN mode\n");
+ break;
+ }
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using %d MB AGP aperture\n", info->agpSize);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using %d MB for the ring buffer\n", info->ringSize);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using %d MB for vertex/indirect buffers\n", info->bufSize);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using %d MB for AGP textures\n", info->agpTexSize);
+
+ /* Try for front, back, depth, and three framebuffers worth of
+ * pixmap cache. Should be enough for a fullscreen background
+ * image plus some leftovers.
+ */
+ info->textureSize = info->FbMapSize - 5 * bufferSize - depthSize;
+
+ /* If that gives us less than half the available memory, let's
+ * be greedy and grab some more. Sorry, I care more about 3D
+ * performance than playing nicely, and you'll get around a full
+ * framebuffer's worth of pixmap cache anyway.
+ */
+ if (info->textureSize < (int)info->FbMapSize / 2) {
+ info->textureSize = info->FbMapSize - 4 * bufferSize - depthSize;
+ }
+ if (info->textureSize < (int)info->FbMapSize / 2) {
+ info->textureSize = info->FbMapSize - 3 * bufferSize - depthSize;
+ }
+ /* If there's still no space for textures, try without pixmap cache */
+ if (info->textureSize < 0) {
+ info->textureSize = info->FbMapSize - 2 * bufferSize - depthSize
+ - 64/4*64;
+ }
+
+ /* Check to see if there is more room available after the 8192nd
+ scanline for textures */
+ if ((int)info->FbMapSize - 8192*width_bytes - bufferSize - depthSize
+ > info->textureSize) {
+ info->textureSize =
+ info->FbMapSize - 8192*width_bytes - bufferSize - depthSize;
+ }
+
+ /* If backbuffer is disabled, don't allocate memory for it */
+ if (info->noBackBuffer) {
+ info->textureSize += bufferSize;
+ }
+
+ if (info->textureSize > 0) {
+ l = RADEONMinBits((info->textureSize-1) / RADEON_NR_TEX_REGIONS);
+ if (l < RADEON_LOG_TEX_GRANULARITY) l = RADEON_LOG_TEX_GRANULARITY;
+
+ /* Round the texture size up to the nearest whole number of
+ * texture regions. Again, be greedy about this, don't
+ * round down.
+ */
+ info->log2TexGran = l;
+ info->textureSize = (info->textureSize >> l) << l;
+ } else {
+ info->textureSize = 0;
+ }
+
+ /* Set a minimum usable local texture heap size. This will fit
+ * two 256x256x32bpp textures.
+ */
+ if (info->textureSize < 512 * 1024) {
+ info->textureOffset = 0;
+ info->textureSize = 0;
+ }
+
+ /* Reserve space for textures */
+ info->textureOffset = ((info->FbMapSize - info->textureSize +
+ RADEON_BUFFER_ALIGN) &
+ ~(CARD32)RADEON_BUFFER_ALIGN);
+
+ /* Reserve space for the shared depth
+ * buffer.
+ */
+ info->depthOffset = ((info->textureOffset - depthSize +
+ RADEON_BUFFER_ALIGN) &
+ ~(CARD32)RADEON_BUFFER_ALIGN);
+ info->depthPitch = pScrn->displayWidth;
+
+ /* Reserve space for the shared back buffer */
+ if (info->noBackBuffer) {
+ info->backOffset = info->depthOffset;
+ info->backPitch = pScrn->displayWidth;
+ } else {
+ info->backOffset = ((info->depthOffset - bufferSize +
+ RADEON_BUFFER_ALIGN) &
+ ~(CARD32)RADEON_BUFFER_ALIGN);
+ info->backPitch = pScrn->displayWidth;
+ }
+
+ info->backY = info->backOffset / width_bytes;
+ info->backX = (info->backOffset - (info->backY * width_bytes)) / cpp;
+
+ scanlines = info->FbMapSize / width_bytes;
+ if (scanlines > 8191) scanlines = 8191;
+
+ MemBox.x1 = 0;
+ MemBox.y1 = 0;
+ MemBox.x2 = pScrn->displayWidth;
+ MemBox.y2 = scanlines;
+
+ if (!xf86InitFBManager(pScreen, &MemBox)) {
+ xf86DrvMsg(scrnIndex, X_ERROR,
+ "Memory manager initialization to "
+ "(%d,%d) (%d,%d) failed\n",
+ MemBox.x1, MemBox.y1, MemBox.x2, MemBox.y2);
+ return FALSE;
+ } else {
+ int width, height;
+
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Memory manager initialized to (%d,%d) (%d,%d)\n",
+ MemBox.x1, MemBox.y1, MemBox.x2, MemBox.y2);
+ if ((fbarea = xf86AllocateOffscreenArea(pScreen,
+ pScrn->displayWidth,
+ 2, 0, NULL, NULL,
+ NULL))) {
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Reserved area from (%d,%d) to (%d,%d)\n",
+ fbarea->box.x1, fbarea->box.y1,
+ fbarea->box.x2, fbarea->box.y2);
+ } else {
+ xf86DrvMsg(scrnIndex, X_ERROR, "Unable to reserve area\n");
+ }
+ if (xf86QueryLargestOffscreenArea(pScreen, &width,
+ &height, 0, 0, 0)) {
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Largest offscreen area available: %d x %d\n",
+ width, height);
+
+ /* Lines in offscreen area needed for depth buffer and
+ * textures
+ */
+ info->depthTexLines = (scanlines
+ - info->depthOffset / width_bytes);
+ info->backLines = (scanlines
+ - info->backOffset / width_bytes
+ - info->depthTexLines);
+ info->backArea = NULL;
+ } else {
+ xf86DrvMsg(scrnIndex, X_ERROR,
+ "Unable to determine largest offscreen area "
+ "available\n");
+ return FALSE;
+ }
+ }
+
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Will use back buffer at offset 0x%x\n",
+ info->backOffset);
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Will use depth buffer at offset 0x%x\n",
+ info->depthOffset);
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Will use %d kb for textures at offset 0x%x\n",
+ info->textureSize/1024, info->textureOffset);
+
+ info->frontPitchOffset = (((info->frontPitch * cpp / 64) << 22) |
+ (info->frontOffset >> 10));
+
+ info->backPitchOffset = (((info->backPitch * cpp / 64) << 22) |
+ (info->backOffset >> 10));
+
+ info->depthPitchOffset = (((info->depthPitch * cpp / 64) << 22) |
+ (info->depthOffset >> 10));
+ } else
+#endif
+ {
+ MemBox.x1 = 0;
+ MemBox.y1 = 0;
+ MemBox.x2 = pScrn->displayWidth;
+ y2 = (info->FbMapSize
+ / (pScrn->displayWidth *
+ info->CurrentLayout.pixel_bytes));
+ if (y2 >= 32768) y2 = 32767; /* because MemBox.y2 is signed short */
+ MemBox.y2 = y2;
+
+ /* The acceleration engine uses 14 bit
+ signed coordinates, so we can't have any
+ drawable caches beyond this region. */
+ if (MemBox.y2 > 8191) MemBox.y2 = 8191;
+
+ if (!xf86InitFBManager(pScreen, &MemBox)) {
+ xf86DrvMsg(scrnIndex, X_ERROR,
+ "Memory manager initialization to "
+ "(%d,%d) (%d,%d) failed\n",
+ MemBox.x1, MemBox.y1, MemBox.x2, MemBox.y2);
+ return FALSE;
+ } else {
+ int width, height;
+ FBAreaPtr fbarea;
+
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Memory manager initialized to (%d,%d) (%d,%d)\n",
+ MemBox.x1, MemBox.y1, MemBox.x2, MemBox.y2);
+ if ((fbarea = xf86AllocateOffscreenArea(pScreen,
+ pScrn->displayWidth,
+ 2, 0, NULL, NULL,
+ NULL))) {
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Reserved area from (%d,%d) to (%d,%d)\n",
+ fbarea->box.x1, fbarea->box.y1,
+ fbarea->box.x2, fbarea->box.y2);
+ } else {
+ xf86DrvMsg(scrnIndex, X_ERROR, "Unable to reserve area\n");
+ }
+ if (xf86QueryLargestOffscreenArea(pScreen, &width, &height,
+ 0, 0, 0)) {
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Largest offscreen area available: %d x %d\n",
+ width, height);
+ }
+ }
+ }
+
+ /* Acceleration setup */
+ if (!xf86ReturnOptValBool(info->Options, OPTION_NOACCEL, FALSE)) {
+ if (RADEONAccelInit(pScreen)) {
+ xf86DrvMsg(scrnIndex, X_INFO, "Acceleration enabled\n");
+ info->accelOn = TRUE;
+
+ /* FIXME: Figure out why this was added because it shouldn't be! */
+ /* This is needed by the DRI and XAA code for shared entities */
+ pScrn->pScreen = pScreen;
+ } else {
+ xf86DrvMsg(scrnIndex, X_ERROR,
+ "Acceleration initialization failed\n");
+ xf86DrvMsg(scrnIndex, X_INFO, "Acceleration disabled\n");
+ info->accelOn = FALSE;
+ }
+ } else {
+ xf86DrvMsg(scrnIndex, X_INFO, "Acceleration disabled\n");
+ info->accelOn = FALSE;
+ }
+
+ /* DGA setup */
+ RADEONDGAInit(pScreen);
+
+ /* Backing store setup */
+ miInitializeBackingStore(pScreen);
+ xf86SetBackingStore(pScreen);
+
+ /* Set Silken Mouse */
+ xf86SetSilkenMouse(pScreen);
+
+ /* Cursor setup */
+ miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
+
+ /* Hardware cursor setup */
+ if (!xf86ReturnOptValBool(info->Options, OPTION_SW_CURSOR, FALSE)) {
+ if (RADEONCursorInit(pScreen)) {
+ int width, height;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+ "Using hardware cursor (scanline %d)\n",
+ info->cursor_start / pScrn->displayWidth
+ / info->CurrentLayout.pixel_bytes);
+ if (xf86QueryLargestOffscreenArea(pScreen, &width, &height,
+ 0, 0, 0)) {
+ xf86DrvMsg(scrnIndex, X_INFO,
+ "Largest offscreen area available: %d x %d\n",
+ width, height);
+ }
+ } else {
+ xf86DrvMsg(scrnIndex, X_ERROR,
+ "Hardware cursor initialization failed\n");
+ xf86DrvMsg(scrnIndex, X_INFO, "Using software cursor\n");
+ }
+ } else {
+ info->cursor_start = 0;
+ xf86DrvMsg(scrnIndex, X_INFO, "Using software cursor\n");
+ }
+
+ /* Colormap setup */
+ if (!miCreateDefColormap(pScreen)) return FALSE;
+ if (!xf86HandleColormaps(pScreen, 256, info->dac6bits ? 6 : 8,
+ RADEONLoadPalette, NULL,
+ CMAP_PALETTED_TRUECOLOR
+#if 0 /* This option messes up text mode! (eich@suse.de) */
+ | CMAP_LOAD_EVEN_IF_OFFSCREEN
+#endif
+ | CMAP_RELOAD_ON_MODE_SWITCH)) return FALSE;
+
+ /* DPMS setup */
+#ifdef DPMSExtension
+ xf86DPMSInit(pScreen, RADEONDisplayPowerManagementSet, 0);
+#endif
+
+ RADEONInitVideo(pScreen);
+
+ /* Provide SaveScreen */
+ pScreen->SaveScreen = RADEONSaveScreen;
+
+ /* Wrap CloseScreen */
+ info->CloseScreen = pScreen->CloseScreen;
+ pScreen->CloseScreen = RADEONCloseScreen;
+
+ /* Note unused options */
+ if (serverGeneration == 1)
+ xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
+
+#ifdef XF86DRI
+ /* DRI finalization */
+ if (info->directRenderingEnabled) {
+ /* Now that mi, cfb, drm and others have
+ done their thing, complete the DRI
+ setup. */
+ info->directRenderingEnabled = RADEONDRIFinishScreenInit(pScreen);
+ }
+ if (info->directRenderingEnabled) {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct rendering enabled\n");
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct rendering disabled\n");
+ }
+#endif
+
+ info->BlockHandler = pScreen->BlockHandler;
+ pScreen->BlockHandler = RADEONBlockHandler;
+
+ return TRUE;
+}
+
+/* Write common registers (initialized to 0) */
+static void RADEONRestoreCommonRegisters(ScrnInfoPtr pScrn,
+ RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ OUTREG(RADEON_OVR_CLR, restore->ovr_clr);
+ OUTREG(RADEON_OVR_WID_LEFT_RIGHT, restore->ovr_wid_left_right);
+ OUTREG(RADEON_OVR_WID_TOP_BOTTOM, restore->ovr_wid_top_bottom);
+ OUTREG(RADEON_OV0_SCALE_CNTL, restore->ov0_scale_cntl);
+ OUTREG(RADEON_SUBPIC_CNTL, restore->subpic_cntl);
+ OUTREG(RADEON_VIPH_CONTROL, restore->viph_control);
+ OUTREG(RADEON_I2C_CNTL_1, restore->i2c_cntl_1);
+ OUTREG(RADEON_GEN_INT_CNTL, restore->gen_int_cntl);
+ OUTREG(RADEON_CAP0_TRIG_CNTL, restore->cap0_trig_cntl);
+ OUTREG(RADEON_CAP1_TRIG_CNTL, restore->cap1_trig_cntl);
+ OUTREG(RADEON_BUS_CNTL, restore->bus_cntl);
+ OUTREG(RADEON_SURFACE_CNTL, restore->surface_cntl);
+
+ /* Workaround for the VT switching problem in dual-head mode. This
+ * problem only occurs on RV style chips, typically when a FP and
+ * CRT are connected.
+ */
+ if (info->HasCRTC2 &&
+ !info->IsSwitching &&
+ info->ChipFamily != CHIP_FAMILY_R200 &&
+ info->ChipFamily != CHIP_FAMILY_R300) {
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+ CARD32 tmp;
+
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0], gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+
+ if (pRADEONEnt->HasSecondary || info->Clone) {
+ tmp = INREG(RADEON_DAC_CNTL2);
+ OUTREG(RADEON_DAC_CNTL2, tmp & ~RADEON_DAC2_DAC_CLK_SEL);
+ usleep(100000);
+ }
+ }
+}
+
+/* Write miscellaneous registers which might have been destroyed by an fbdevHW
+ * call
+ */
+static void RADEONRestoreFBDevRegisters(ScrnInfoPtr pScrn,
+ RADEONSavePtr restore)
+{
+#ifdef XF86DRI
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ /* Restore register for vertical blank interrupts */
+ if (info->irq) {
+ OUTREG(RADEON_GEN_INT_CNTL, restore->gen_int_cntl);
+ }
+
+ /* Restore registers for page flipping */
+ if (info->allowPageFlip) {
+ OUTREG(RADEON_CRTC_OFFSET_CNTL, restore->crtc_offset_cntl);
+ if (info->HasCRTC2) {
+ OUTREG(RADEON_CRTC2_OFFSET_CNTL, restore->crtc2_offset_cntl);
+ }
+ }
+#endif
+}
+
+/* Write CRTC registers */
+static void RADEONRestoreCrtcRegisters(ScrnInfoPtr pScrn,
+ RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ OUTREG(RADEON_CRTC_GEN_CNTL, restore->crtc_gen_cntl);
+
+ OUTREGP(RADEON_CRTC_EXT_CNTL,
+ restore->crtc_ext_cntl,
+ RADEON_CRTC_VSYNC_DIS |
+ RADEON_CRTC_HSYNC_DIS |
+ RADEON_CRTC_DISPLAY_DIS);
+
+ OUTREGP(RADEON_DAC_CNTL,
+ restore->dac_cntl,
+ RADEON_DAC_RANGE_CNTL |
+ RADEON_DAC_BLANKING);
+
+ OUTREG(RADEON_CRTC_H_TOTAL_DISP, restore->crtc_h_total_disp);
+ OUTREG(RADEON_CRTC_H_SYNC_STRT_WID, restore->crtc_h_sync_strt_wid);
+ OUTREG(RADEON_CRTC_V_TOTAL_DISP, restore->crtc_v_total_disp);
+ OUTREG(RADEON_CRTC_V_SYNC_STRT_WID, restore->crtc_v_sync_strt_wid);
+ OUTREG(RADEON_CRTC_OFFSET, restore->crtc_offset);
+ OUTREG(RADEON_CRTC_OFFSET_CNTL, restore->crtc_offset_cntl);
+ OUTREG(RADEON_CRTC_PITCH, restore->crtc_pitch);
+}
+
+/* Write CRTC2 registers */
+static void RADEONRestoreCrtc2Registers(ScrnInfoPtr pScrn,
+ RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ restore->crtc2_gen_cntl,
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_DIS |
+ RADEON_CRTC2_DISP_DIS);
+
+ OUTREG(RADEON_DAC_CNTL2, restore->dac2_cntl);
+
+ if (info->ChipFamily == CHIP_FAMILY_R200 ||
+ info->ChipFamily == CHIP_FAMILY_R300) {
+ OUTREG(RADEON_DISP_OUTPUT_CNTL, restore->disp_output_cntl);
+ } else {
+ OUTREG(RADEON_DISP_HW_DEBUG, restore->disp_hw_debug);
+ if (info->IsDell) {
+ /* Workaround for DELL card. BIOS doesn't initialize
+ * TV_DAC_CNTL to a correct value which causes too high
+ * contrast for the second CRT (using TV_DAC).
+ */
+ OUTREG(RADEON_TV_DAC_CNTL, 0x00280203);
+ }
+ }
+
+ OUTREG(RADEON_CRTC2_H_TOTAL_DISP, restore->crtc2_h_total_disp);
+ OUTREG(RADEON_CRTC2_H_SYNC_STRT_WID, restore->crtc2_h_sync_strt_wid);
+ OUTREG(RADEON_CRTC2_V_TOTAL_DISP, restore->crtc2_v_total_disp);
+ OUTREG(RADEON_CRTC2_V_SYNC_STRT_WID, restore->crtc2_v_sync_strt_wid);
+ OUTREG(RADEON_CRTC2_OFFSET, restore->crtc2_offset);
+ OUTREG(RADEON_CRTC2_OFFSET_CNTL, restore->crtc2_offset_cntl);
+ OUTREG(RADEON_CRTC2_PITCH, restore->crtc2_pitch);
+
+ if (info->DisplayType == MT_DFP || info->CloneType == MT_DFP) {
+ OUTREG(RADEON_FP_H2_SYNC_STRT_WID, restore->fp2_h_sync_strt_wid);
+ OUTREG(RADEON_FP_V2_SYNC_STRT_WID, restore->fp2_v_sync_strt_wid);
+ OUTREG(RADEON_FP2_GEN_CNTL, restore->fp2_gen_cntl);
+ }
+
+#if 0
+ /* Hack for restoring text mode -- fixed elsewhere */
+ usleep(100000);
+#endif
+}
+
+/* Write flat panel registers */
+static void RADEONRestoreFPRegisters(ScrnInfoPtr pScrn, RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+ unsigned long tmp;
+
+ OUTREG(RADEON_FP_CRTC_H_TOTAL_DISP, restore->fp_crtc_h_total_disp);
+ OUTREG(RADEON_FP_CRTC_V_TOTAL_DISP, restore->fp_crtc_v_total_disp);
+ OUTREG(RADEON_FP_H_SYNC_STRT_WID, restore->fp_h_sync_strt_wid);
+ OUTREG(RADEON_FP_V_SYNC_STRT_WID, restore->fp_v_sync_strt_wid);
+ OUTREG(RADEON_TMDS_PLL_CNTL, restore->tmds_pll_cntl);
+ OUTREG(RADEON_FP_HORZ_STRETCH, restore->fp_horz_stretch);
+ OUTREG(RADEON_FP_VERT_STRETCH, restore->fp_vert_stretch);
+ OUTREG(RADEON_FP_GEN_CNTL, restore->fp_gen_cntl);
+
+ if (info->DisplayType == MT_LCD) {
+ tmp = INREG(RADEON_LVDS_GEN_CNTL);
+ if ((tmp & (RADEON_LVDS_ON | RADEON_LVDS_BLON)) ==
+ (restore->lvds_gen_cntl & (RADEON_LVDS_ON | RADEON_LVDS_BLON))) {
+ OUTREG(RADEON_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
+ } else {
+ if (restore->lvds_gen_cntl & (RADEON_LVDS_ON | RADEON_LVDS_BLON)) {
+ usleep(RADEONPTR(pScrn)->PanelPwrDly * 1000);
+ OUTREG(RADEON_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
+ } else {
+ OUTREG(RADEON_LVDS_GEN_CNTL,
+ restore->lvds_gen_cntl | RADEON_LVDS_BLON);
+ usleep(RADEONPTR(pScrn)->PanelPwrDly * 1000);
+ OUTREG(RADEON_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
+ }
+ }
+ }
+}
+
+static void RADEONPLLWaitForReadUpdateComplete(ScrnInfoPtr pScrn)
+{
+ int i = 0;
+
+ /* FIXME: Certain revisions of R300 can't recover here. Not sure of
+ the cause yet, but this workaround will mask the problem for now.
+ Other chips usually will pass at the very first test, so the
+ workaround shouldn't have any effect on them. */
+ for (i = 0;
+ (i < 10000 &&
+ INPLL(pScrn, RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R);
+ i++);
+}
+
+static void RADEONPLLWriteUpdate(ScrnInfoPtr pScrn)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ while (INPLL(pScrn, RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R);
+
+ OUTPLLP(pScrn, RADEON_PPLL_REF_DIV,
+ RADEON_PPLL_ATOMIC_UPDATE_W,
+ ~(RADEON_PPLL_ATOMIC_UPDATE_W));
+}
+
+static void RADEONPLL2WaitForReadUpdateComplete(ScrnInfoPtr pScrn)
+{
+ int i = 0;
+
+ /* FIXME: Certain revisions of R300 can't recover here. Not sure of
+ the cause yet, but this workaround will mask the problem for now.
+ Other chips usually will pass at the very first test, so the
+ workaround shouldn't have any effect on them. */
+ for (i = 0;
+ (i < 10000 &&
+ INPLL(pScrn, RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R);
+ i++);
+}
+
+static void RADEONPLL2WriteUpdate(ScrnInfoPtr pScrn)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ while (INPLL(pScrn, RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R);
+
+ OUTPLLP(pScrn, RADEON_P2PLL_REF_DIV,
+ RADEON_P2PLL_ATOMIC_UPDATE_W,
+ ~(RADEON_P2PLL_ATOMIC_UPDATE_W));
+}
+
+/* Write PLL registers */
+static void RADEONRestorePLLRegisters(ScrnInfoPtr pScrn,
+ RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ OUTPLLP(pScrn, RADEON_VCLK_ECP_CNTL,
+ RADEON_VCLK_SRC_SEL_CPUCLK,
+ ~(RADEON_VCLK_SRC_SEL_MASK));
+
+ OUTPLLP(pScrn,
+ RADEON_PPLL_CNTL,
+ RADEON_PPLL_RESET
+ | RADEON_PPLL_ATOMIC_UPDATE_EN
+ | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN,
+ ~(RADEON_PPLL_RESET
+ | RADEON_PPLL_ATOMIC_UPDATE_EN
+ | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN));
+
+ OUTREGP(RADEON_CLOCK_CNTL_INDEX,
+ RADEON_PLL_DIV_SEL,
+ ~(RADEON_PLL_DIV_SEL));
+
+ if (info->ChipFamily == CHIP_FAMILY_R300) {
+ if (restore->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) {
+ /* When restoring console mode, use saved PPLL_REF_DIV
+ * setting.
+ */
+ OUTPLLP(pScrn, RADEON_PPLL_REF_DIV,
+ restore->ppll_ref_div,
+ 0);
+ } else {
+ /* R300 uses ref_div_acc field as real ref divider */
+ OUTPLLP(pScrn, RADEON_PPLL_REF_DIV,
+ (restore->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT),
+ ~R300_PPLL_REF_DIV_ACC_MASK);
+ }
+ } else {
+ OUTPLLP(pScrn, RADEON_PPLL_REF_DIV,
+ restore->ppll_ref_div,
+ ~RADEON_PPLL_REF_DIV_MASK);
+ }
+
+ OUTPLLP(pScrn, RADEON_PPLL_DIV_3,
+ restore->ppll_div_3,
+ ~RADEON_PPLL_FB3_DIV_MASK);
+
+ OUTPLLP(pScrn, RADEON_PPLL_DIV_3,
+ restore->ppll_div_3,
+ ~RADEON_PPLL_POST3_DIV_MASK);
+
+ RADEONPLLWriteUpdate(pScrn);
+ RADEONPLLWaitForReadUpdateComplete(pScrn);
+
+ OUTPLL(RADEON_HTOTAL_CNTL, restore->htotal_cntl);
+
+ OUTPLLP(pScrn, RADEON_PPLL_CNTL,
+ 0,
+ ~(RADEON_PPLL_RESET
+ | RADEON_PPLL_SLEEP
+ | RADEON_PPLL_ATOMIC_UPDATE_EN
+ | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN));
+
+ RADEONTRACE(("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n",
+ restore->ppll_ref_div,
+ restore->ppll_div_3,
+ restore->htotal_cntl,
+ INPLL(pScrn, RADEON_PPLL_CNTL)));
+ RADEONTRACE(("Wrote: rd=%d, fd=%d, pd=%d\n",
+ restore->ppll_ref_div & RADEON_PPLL_REF_DIV_MASK,
+ restore->ppll_div_3 & RADEON_PPLL_FB3_DIV_MASK,
+ (restore->ppll_div_3 & RADEON_PPLL_POST3_DIV_MASK) >> 16));
+
+ usleep(5000); /* Let the clock to lock */
+
+ OUTPLLP(pScrn, RADEON_VCLK_ECP_CNTL,
+ RADEON_VCLK_SRC_SEL_PPLLCLK,
+ ~(RADEON_VCLK_SRC_SEL_MASK));
+}
+
+
+/* Write PLL2 registers */
+static void RADEONRestorePLL2Registers(ScrnInfoPtr pScrn,
+ RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ OUTPLLP(pScrn, RADEON_PIXCLKS_CNTL,
+ RADEON_PIX2CLK_SRC_SEL_CPUCLK,
+ ~(RADEON_PIX2CLK_SRC_SEL_MASK));
+
+ OUTPLLP(pScrn,
+ RADEON_P2PLL_CNTL,
+ RADEON_P2PLL_RESET
+ | RADEON_P2PLL_ATOMIC_UPDATE_EN
+ | RADEON_P2PLL_VGA_ATOMIC_UPDATE_EN,
+ ~(RADEON_P2PLL_RESET
+ | RADEON_P2PLL_ATOMIC_UPDATE_EN
+ | RADEON_P2PLL_VGA_ATOMIC_UPDATE_EN));
+
+ OUTPLLP(pScrn, RADEON_P2PLL_REF_DIV,
+ restore->p2pll_ref_div,
+ ~RADEON_P2PLL_REF_DIV_MASK);
+
+ OUTPLLP(pScrn, RADEON_P2PLL_DIV_0,
+ restore->p2pll_div_0,
+ ~RADEON_P2PLL_FB0_DIV_MASK);
+
+ OUTPLLP(pScrn, RADEON_P2PLL_DIV_0,
+ restore->p2pll_div_0,
+ ~RADEON_P2PLL_POST0_DIV_MASK);
+
+ RADEONPLL2WriteUpdate(pScrn);
+ RADEONPLL2WaitForReadUpdateComplete(pScrn);
+
+ OUTPLL(RADEON_HTOTAL2_CNTL, restore->htotal_cntl2);
+
+ OUTPLLP(pScrn, RADEON_P2PLL_CNTL,
+ 0,
+ ~(RADEON_P2PLL_RESET
+ | RADEON_P2PLL_SLEEP
+ | RADEON_P2PLL_ATOMIC_UPDATE_EN
+ | RADEON_P2PLL_VGA_ATOMIC_UPDATE_EN));
+
+ RADEONTRACE(("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n",
+ restore->p2pll_ref_div,
+ restore->p2pll_div_0,
+ restore->htotal_cntl2,
+ INPLL(pScrn, RADEON_P2PLL_CNTL)));
+ RADEONTRACE(("Wrote: rd=%d, fd=%d, pd=%d\n",
+ restore->p2pll_ref_div & RADEON_P2PLL_REF_DIV_MASK,
+ restore->p2pll_div_0 & RADEON_P2PLL_FB0_DIV_MASK,
+ (restore->p2pll_div_0 & RADEON_P2PLL_POST0_DIV_MASK) >>16));
+
+ usleep(5000); /* Let the clock to lock */
+
+ OUTPLLP(pScrn, RADEON_PIXCLKS_CNTL,
+ RADEON_PIX2CLK_SRC_SEL_P2PLLCLK,
+ ~(RADEON_PIX2CLK_SRC_SEL_MASK));
+}
+
+#if 0
+/* Write palette data */
+static void RADEONRestorePalette(ScrnInfoPtr pScrn, RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+ int i;
+
+ if (!restore->palette_valid) return;
+
+ PAL_SELECT(1);
+ OUTPAL_START(0);
+ for (i = 0; i < 256; i++) {
+ RADEONWaitForFifo(pScrn, 32); /* delay */
+ OUTPAL_NEXT_CARD32(restore->palette2[i]);
+ }
+
+ PAL_SELECT(0);
+ OUTPAL_START(0);
+ for (i = 0; i < 256; i++) {
+ RADEONWaitForFifo(pScrn, 32); /* delay */
+ OUTPAL_NEXT_CARD32(restore->palette[i]);
+ }
+}
+#endif
+
+/* Write out state to define a new video mode */
+static void RADEONRestoreMode(ScrnInfoPtr pScrn, RADEONSavePtr restore)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+ static RADEONSaveRec restore0;
+
+ /* For Non-dual head card, we don't have private field in the Entity */
+ if (!info->HasCRTC2) {
+ RADEONRestoreCommonRegisters(pScrn, restore);
+ RADEONRestoreCrtcRegisters(pScrn, restore);
+ if ((info->DisplayType == MT_DFP) ||
+ (info->DisplayType == MT_LCD)) {
+ RADEONRestoreFPRegisters(pScrn, restore);
+ }
+ RADEONRestorePLLRegisters(pScrn, restore);
+ return;
+ }
+
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0], gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+
+ RADEONTRACE(("RADEONRestoreMode(%p)\n", restore));
+
+ /* When changing mode with Dual-head card, care must be taken for
+ * the special order in setting registers. CRTC2 has to be set
+ * before changing CRTC_EXT register. In the dual-head setup, X
+ * server calls this routine twice with primary and secondary pScrn
+ * pointers respectively. The calls can come with different
+ * order. Regardless the order of X server issuing the calls, we
+ * have to ensure we set registers in the right order!!! Otherwise
+ * we may get a blank screen.
+ */
+ if (info->IsSecondary) {
+ if (!pRADEONEnt->RestorePrimary)
+ RADEONRestoreCommonRegisters(pScrn, restore);
+ RADEONRestoreCrtc2Registers(pScrn, restore);
+ RADEONRestorePLL2Registers(pScrn, restore);
+
+ if(info->IsSwitching) return;
+
+ pRADEONEnt->IsSecondaryRestored = TRUE;
+
+ if (pRADEONEnt->RestorePrimary) {
+ RADEONInfoPtr info0 = RADEONPTR(pRADEONEnt->pPrimaryScrn);
+ pRADEONEnt->RestorePrimary = FALSE;
+
+ RADEONRestoreCrtcRegisters(pScrn, &restore0);
+ if ((info0->DisplayType == MT_DFP) ||
+ (info0->DisplayType == MT_LCD)) {
+ RADEONRestoreFPRegisters(pScrn, &restore0);
+ }
+
+ RADEONRestorePLLRegisters(pScrn, &restore0);
+ pRADEONEnt->IsSecondaryRestored = FALSE;
+ }
+ } else {
+ if (!pRADEONEnt->IsSecondaryRestored)
+ RADEONRestoreCommonRegisters(pScrn, restore);
+
+ if (info->Clone) {
+ RADEONRestoreCrtc2Registers(pScrn, restore);
+ RADEONRestorePLL2Registers(pScrn, restore);
+ }
+
+ if (!pRADEONEnt->HasSecondary || pRADEONEnt->IsSecondaryRestored ||
+ info->IsSwitching) {
+ pRADEONEnt->IsSecondaryRestored = FALSE;
+
+ RADEONRestoreCrtcRegisters(pScrn, restore);
+ if ((info->DisplayType == MT_DFP) ||
+ (info->DisplayType == MT_LCD)) {
+ RADEONRestoreFPRegisters(pScrn, restore);
+ }
+ RADEONRestorePLLRegisters(pScrn, restore);
+ } else {
+ memcpy(&restore0, restore, sizeof(restore0));
+ pRADEONEnt->RestorePrimary = TRUE;
+ }
+ }
+
+#if 0
+ RADEONRestorePalette(pScrn, &info->SavedReg);
+#endif
+}
+
+/* Read common registers */
+static void RADEONSaveCommonRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ save->ovr_clr = INREG(RADEON_OVR_CLR);
+ save->ovr_wid_left_right = INREG(RADEON_OVR_WID_LEFT_RIGHT);
+ save->ovr_wid_top_bottom = INREG(RADEON_OVR_WID_TOP_BOTTOM);
+ save->ov0_scale_cntl = INREG(RADEON_OV0_SCALE_CNTL);
+ save->subpic_cntl = INREG(RADEON_SUBPIC_CNTL);
+ save->viph_control = INREG(RADEON_VIPH_CONTROL);
+ save->i2c_cntl_1 = INREG(RADEON_I2C_CNTL_1);
+ save->gen_int_cntl = INREG(RADEON_GEN_INT_CNTL);
+ save->cap0_trig_cntl = INREG(RADEON_CAP0_TRIG_CNTL);
+ save->cap1_trig_cntl = INREG(RADEON_CAP1_TRIG_CNTL);
+ save->bus_cntl = INREG(RADEON_BUS_CNTL);
+ save->surface_cntl = INREG(RADEON_SURFACE_CNTL);
+}
+
+/* Read miscellaneous registers which might be destroyed by an fbdevHW call */
+static void RADEONSaveFBDevRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+#ifdef XF86DRI
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ /* Save register for vertical blank interrupts */
+ if (info->irq) {
+ save->gen_int_cntl = INREG(RADEON_GEN_INT_CNTL);
+ }
+
+ /* Save registers for page flipping */
+ if (info->allowPageFlip) {
+ save->crtc_offset_cntl = INREG(RADEON_CRTC_OFFSET_CNTL);
+ if (info->HasCRTC2) {
+ save->crtc2_offset_cntl = INREG(RADEON_CRTC2_OFFSET_CNTL);
+ }
+ }
+#endif
+}
+
+/* Read CRTC registers */
+static void RADEONSaveCrtcRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ save->crtc_gen_cntl = INREG(RADEON_CRTC_GEN_CNTL);
+ save->crtc_ext_cntl = INREG(RADEON_CRTC_EXT_CNTL);
+ save->dac_cntl = INREG(RADEON_DAC_CNTL);
+ save->crtc_h_total_disp = INREG(RADEON_CRTC_H_TOTAL_DISP);
+ save->crtc_h_sync_strt_wid = INREG(RADEON_CRTC_H_SYNC_STRT_WID);
+ save->crtc_v_total_disp = INREG(RADEON_CRTC_V_TOTAL_DISP);
+ save->crtc_v_sync_strt_wid = INREG(RADEON_CRTC_V_SYNC_STRT_WID);
+ save->crtc_offset = INREG(RADEON_CRTC_OFFSET);
+ save->crtc_offset_cntl = INREG(RADEON_CRTC_OFFSET_CNTL);
+ save->crtc_pitch = INREG(RADEON_CRTC_PITCH);
+}
+
+/* Read flat panel registers */
+static void RADEONSaveFPRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ save->fp_crtc_h_total_disp = INREG(RADEON_FP_CRTC_H_TOTAL_DISP);
+ save->fp_crtc_v_total_disp = INREG(RADEON_FP_CRTC_V_TOTAL_DISP);
+ save->fp_gen_cntl = INREG(RADEON_FP_GEN_CNTL);
+ save->fp_h_sync_strt_wid = INREG(RADEON_FP_H_SYNC_STRT_WID);
+ save->fp_horz_stretch = INREG(RADEON_FP_HORZ_STRETCH);
+ save->fp_v_sync_strt_wid = INREG(RADEON_FP_V_SYNC_STRT_WID);
+ save->fp_vert_stretch = INREG(RADEON_FP_VERT_STRETCH);
+ save->lvds_gen_cntl = INREG(RADEON_LVDS_GEN_CNTL);
+ save->lvds_pll_cntl = INREG(RADEON_LVDS_PLL_CNTL);
+ save->tmds_pll_cntl = INREG(RADEON_TMDS_PLL_CNTL);
+}
+
+/* Read CRTC2 registers */
+static void RADEONSaveCrtc2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ save->dac2_cntl = INREG(RADEON_DAC_CNTL2);
+ save->disp_output_cntl = INREG(RADEON_DISP_OUTPUT_CNTL);
+ save->disp_hw_debug = INREG (RADEON_DISP_HW_DEBUG);
+
+ save->crtc2_gen_cntl = INREG(RADEON_CRTC2_GEN_CNTL);
+ save->crtc2_h_total_disp = INREG(RADEON_CRTC2_H_TOTAL_DISP);
+ save->crtc2_h_sync_strt_wid = INREG(RADEON_CRTC2_H_SYNC_STRT_WID);
+ save->crtc2_v_total_disp = INREG(RADEON_CRTC2_V_TOTAL_DISP);
+ save->crtc2_v_sync_strt_wid = INREG(RADEON_CRTC2_V_SYNC_STRT_WID);
+ save->crtc2_offset = INREG(RADEON_CRTC2_OFFSET);
+ save->crtc2_offset_cntl = INREG(RADEON_CRTC2_OFFSET_CNTL);
+ save->crtc2_pitch = INREG(RADEON_CRTC2_PITCH);
+
+ save->fp2_h_sync_strt_wid = INREG (RADEON_FP_H2_SYNC_STRT_WID);
+ save->fp2_v_sync_strt_wid = INREG (RADEON_FP_V2_SYNC_STRT_WID);
+ save->fp2_gen_cntl = INREG (RADEON_FP2_GEN_CNTL);
+
+}
+
+/* Read PLL registers */
+static void RADEONSavePLLRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ save->ppll_ref_div = INPLL(pScrn, RADEON_PPLL_REF_DIV);
+ save->ppll_div_3 = INPLL(pScrn, RADEON_PPLL_DIV_3);
+ save->htotal_cntl = INPLL(pScrn, RADEON_HTOTAL_CNTL);
+
+ RADEONTRACE(("Read: 0x%08x 0x%08x 0x%08x\n",
+ save->ppll_ref_div,
+ save->ppll_div_3,
+ save->htotal_cntl));
+ RADEONTRACE(("Read: rd=%d, fd=%d, pd=%d\n",
+ save->ppll_ref_div & RADEON_PPLL_REF_DIV_MASK,
+ save->ppll_div_3 & RADEON_PPLL_FB3_DIV_MASK,
+ (save->ppll_div_3 & RADEON_PPLL_POST3_DIV_MASK) >> 16));
+}
+
+/* Read PLL registers */
+static void RADEONSavePLL2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ save->p2pll_ref_div = INPLL(pScrn, RADEON_P2PLL_REF_DIV);
+ save->p2pll_div_0 = INPLL(pScrn, RADEON_P2PLL_DIV_0);
+ save->htotal_cntl2 = INPLL(pScrn, RADEON_HTOTAL2_CNTL);
+
+ RADEONTRACE(("Read: 0x%08x 0x%08x 0x%08x\n",
+ save->p2pll_ref_div,
+ save->p2pll_div_0,
+ save->htotal_cntl2));
+ RADEONTRACE(("Read: rd=%d, fd=%d, pd=%d\n",
+ save->p2pll_ref_div & RADEON_P2PLL_REF_DIV_MASK,
+ save->p2pll_div_0 & RADEON_P2PLL_FB0_DIV_MASK,
+ (save->p2pll_div_0 & RADEON_P2PLL_POST0_DIV_MASK) >> 16));
+}
+
+/* Read palette data */
+static void RADEONSavePalette(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+ int i;
+
+#ifdef ENABLE_FLAT_PANEL
+ /* Select palette 0 (main CRTC) if using FP-enabled chip */
+ /* if (info->Port1 == MT_DFP) PAL_SELECT(1); */
+#endif
+ PAL_SELECT(1);
+ INPAL_START(0);
+ for (i = 0; i < 256; i++) save->palette2[i] = INPAL_NEXT();
+ PAL_SELECT(0);
+ INPAL_START(0);
+ for (i = 0; i < 256; i++) save->palette[i] = INPAL_NEXT();
+ save->palette_valid = TRUE;
+}
+
+/* Save state that defines current video mode */
+static void RADEONSaveMode(ScrnInfoPtr pScrn, RADEONSavePtr save)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+
+ RADEONTRACE(("RADEONSaveMode(%p)\n", save));
+ if (info->IsSecondary) {
+ RADEONSaveCrtc2Registers(pScrn, save);
+ RADEONSavePLL2Registers(pScrn, save);
+ } else {
+ RADEONSavePLLRegisters(pScrn, save);
+ RADEONSaveCommonRegisters(pScrn, save);
+ RADEONSaveCrtcRegisters(pScrn, save);
+
+ if ((info->DisplayType == MT_DFP) ||
+ (info->DisplayType == MT_LCD)) {
+ RADEONSaveFPRegisters(pScrn, save);
+ }
+
+ if (info->Clone) {
+ RADEONSaveCrtc2Registers(pScrn, save);
+ RADEONSavePLL2Registers(pScrn, save);
+ }
+ /* RADEONSavePalette(pScrn, save); */
+ }
+
+ RADEONTRACE(("RADEONSaveMode returns %p\n", save));
+}
+
+/* Save everything needed to restore the original VC state */
+static void RADEONSave(ScrnInfoPtr pScrn)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+ RADEONSavePtr save = &info->SavedReg;
+ vgaHWPtr hwp = VGAHWPTR(pScrn);
+
+ RADEONTRACE(("RADEONSave\n"));
+ if (info->FBDev) {
+ fbdevHWSave(pScrn);
+ return;
+ }
+
+ if (!info->IsSecondary) {
+ vgaHWUnlock(hwp);
+#if defined(__powerpc__)
+ /* temporary hack to prevent crashing on PowerMacs when trying to
+ * read VGA fonts and colormap, will find a better solution
+ * in the future
+ */
+ vgaHWSave(pScrn, &hwp->SavedReg, VGA_SR_MODE); /* Save mode only */
+#else
+ vgaHWSave(pScrn, &hwp->SavedReg, VGA_SR_ALL); /* Save mode
+ * & fonts & cmap
+ */
+#endif
+ vgaHWLock(hwp);
+ save->dp_datatype = INREG(RADEON_DP_DATATYPE);
+ save->rbbm_soft_reset = INREG(RADEON_RBBM_SOFT_RESET);
+ save->clock_cntl_index = INREG(RADEON_CLOCK_CNTL_INDEX);
+ if (info->R300CGWorkaround) R300CGWorkaround(pScrn);
+ }
+
+ RADEONSaveMode(pScrn, save);
+}
+
+/* Restore the original (text) mode */
+static void RADEONRestore(ScrnInfoPtr pScrn)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+ RADEONSavePtr restore = &info->SavedReg;
+ vgaHWPtr hwp = VGAHWPTR(pScrn);
+
+ RADEONTRACE(("RADEONRestore\n"));
+
+#if X_BYTE_ORDER == X_BIG_ENDIAN
+ RADEONWaitForFifo(pScrn, 1);
+ OUTREG(RADEON_RBBM_GUICNTL, RADEON_HOST_DATA_SWAP_NONE);
+#endif
+
+ if (info->FBDev) {
+ fbdevHWRestore(pScrn);
+ return;
+ }
+ RADEONBlank(pScrn);
+
+ OUTREG(RADEON_CLOCK_CNTL_INDEX, restore->clock_cntl_index);
+ if (info->R300CGWorkaround) R300CGWorkaround(pScrn);
+ OUTREG(RADEON_RBBM_SOFT_RESET, restore->rbbm_soft_reset);
+ OUTREG(RADEON_DP_DATATYPE, restore->dp_datatype);
+
+#if 0
+ /* M6 card has trouble restoring text mode for its CRT.
+ * This is fixed elsewhere and will be removed in the future.
+ */
+ if ((xf86IsEntityShared(pScrn->entityList[0]) || info->Clone)
+ && info->IsM6)
+ OUTREG(RADEON_DAC_CNTL2, restore->dac2_cntl);
+#endif
+
+ RADEONRestoreMode(pScrn, restore);
+
+#if 0
+ /* Temp fix to "solve" VT switch problems. When switching VTs on
+ * some systems, the console can either hang or the fonts can be
+ * corrupted. This hack solves the problem 99% of the time. A
+ * correct fix is being worked on.
+ */
+ usleep(100000);
+#endif
+
+ if (!info->IsSecondary) {
+ vgaHWUnlock(hwp);
+#if defined(__powerpc__)
+ /* Temporary hack to prevent crashing on PowerMacs when trying to
+ * write VGA fonts, will find a better solution in the future
+ */
+ vgaHWRestore(pScrn, &hwp->SavedReg, VGA_SR_MODE );
+#else
+ vgaHWRestore(pScrn, &hwp->SavedReg, VGA_SR_MODE | VGA_SR_FONTS );
+#endif
+ vgaHWLock(hwp);
+ } else {
+ DevUnion *pPriv;
+ RADEONEntPtr pRADEONEnt;
+ ScrnInfoPtr pScrn0;
+ vgaHWPtr hwp0;
+
+ pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+ gRADEONEntityIndex);
+ pRADEONEnt = pPriv->ptr;
+
+ pScrn0 = pRADEONEnt->pPrimaryScrn;
+ hwp0 = VGAHWPTR(pScrn0);
+ vgaHWUnlock(hwp0);
+ vgaHWRestore(pScrn0, &hwp0->SavedReg, VGA_SR_MODE | VGA_SR_FONTS );
+ vgaHWLock(hwp0);
+ }
+ RADEONUnblank(pScrn);
+
+#if 0
+ RADEONWaitForVerticalSync(pScrn);
+#endif
+}
+
+/* Define common registers for requested video mode */
+static void RADEONInitCommonRegisters(RADEONSavePtr save, RADEONInfoPtr info)
+{
+ save->ovr_clr = 0;
+ save->ovr_wid_left_right = 0;
+ save->ovr_wid_top_bottom = 0;
+ save->ov0_scale_cntl = 0;
+ save->subpic_cntl = 0;
+ save->viph_control = 0;
+ save->i2c_cntl_1 = 0;
+ save->rbbm_soft_reset = 0;
+ save->cap0_trig_cntl = 0;
+ save->cap1_trig_cntl = 0;
+ save->bus_cntl = info->BusCntl;
+ /*
+ * If bursts are enabled, turn on discards
+ * Radeon doesn't have write bursts
+ */
+ if (save->bus_cntl & (RADEON_BUS_READ_BURST))
+ save->bus_cntl |= RADEON_BUS_RD_DISCARD_EN;
+}
+
+/* Define CRTC registers for requested video mode */
+static Bool RADEONInitCrtcRegisters(ScrnInfoPtr pScrn, RADEONSavePtr save,
+ DisplayModePtr mode, RADEONInfoPtr info)
+{
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ int format;
+ int hsync_start;
+ int hsync_wid;
+ int hsync_fudge;
+ int vsync_wid;
+ int bytpp;
+ int hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
+ int hsync_fudge_fp[] = { 0x02, 0x02, 0x00, 0x00, 0x05, 0x05 };
+
+ switch (info->CurrentLayout.pixel_code) {
+ case 4: format = 1; bytpp = 0; break;
+ case 8: format = 2; bytpp = 1; break;
+ case 15: format = 3; bytpp = 2; break; /* 555 */
+ case 16: format = 4; bytpp = 2; break; /* 565 */
+ case 24: format = 5; bytpp = 3; break; /* RGB */
+ case 32: format = 6; bytpp = 4; break; /* xRGB */
+ default:
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Unsupported pixel depth (%d)\n",
+ info->CurrentLayout.bitsPerPixel);
+ return FALSE;
+ }
+ RADEONTRACE(("Format = %d (%d bytes per pixel)\n", format, bytpp));
+
+ if ((info->DisplayType == MT_DFP) ||
+ (info->DisplayType == MT_LCD)) {
+ hsync_fudge = hsync_fudge_fp[format-1];
+ if (mode->Flags & RADEON_USE_RMX) {
+#if 0
+ mode->CrtcHDisplay = info->PanelXRes;
+ mode->CrtcVDisplay = info->PanelYRes;
+#endif
+ mode->CrtcHTotal = mode->CrtcHDisplay + info->HBlank;
+ mode->CrtcHSyncStart = mode->CrtcHDisplay + info->HOverPlus;
+ mode->CrtcHSyncEnd = mode->CrtcHSyncStart + info->HSyncWidth;
+ mode->CrtcVTotal = mode->CrtcVDisplay + info->VBlank;
+ mode->CrtcVSyncStart = mode->CrtcVDisplay + info->VOverPlus;
+ mode->CrtcVSyncEnd = mode->CrtcVSyncStart + info->VSyncWidth;
+ mode->Clock = info->DotClock;
+ mode->Flags = info->Flags | RADEON_USE_RMX;
+ }
+ } else {
+ hsync_fudge = hsync_fudge_default[format-1];
+ }
+
+ save->crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN
+ | RADEON_CRTC_EN
+ | (format << 8)
+ | ((mode->Flags & V_DBLSCAN)
+ ? RADEON_CRTC_DBL_SCAN_EN
+ : 0)
+ | ((mode->Flags & V_CSYNC)
+ ? RADEON_CRTC_CSYNC_EN
+ : 0)
+ | ((mode->Flags & V_INTERLACE)
+ ? RADEON_CRTC_INTERLACE_EN
+ : 0));
+
+ if ((info->DisplayType == MT_DFP) ||
+ (info->DisplayType == MT_LCD)) {
+ save->crtc_ext_cntl = RADEON_VGA_ATI_LINEAR | RADEON_XCRT_CNT_EN;
+ save->crtc_gen_cntl &= ~(RADEON_CRTC_DBL_SCAN_EN |
+ RADEON_CRTC_CSYNC_EN |
+ RADEON_CRTC_INTERLACE_EN);
+ } else {
+ save->crtc_ext_cntl = (RADEON_VGA_ATI_LINEAR |
+ RADEON_XCRT_CNT_EN |
+ RADEON_CRTC_CRT_ON);
+ }
+
+ save->dac_cntl = (RADEON_DAC_MASK_ALL
+ | RADEON_DAC_VGA_ADR_EN
+ | (info->dac6bits ? 0 : RADEON_DAC_8BIT_EN));
+
+ save->crtc_h_total_disp = ((((mode->CrtcHTotal / 8) - 1) & 0x3ff)
+ | ((((mode->CrtcHDisplay / 8) - 1) & 0x1ff)
+ << 16));
+
+ hsync_wid = (mode->CrtcHSyncEnd - mode->CrtcHSyncStart) / 8;
+ if (!hsync_wid) hsync_wid = 1;
+ hsync_start = mode->CrtcHSyncStart - 8 + hsync_fudge;
+
+ save->crtc_h_sync_strt_wid = ((hsync_start & 0x1fff)
+ | ((hsync_wid & 0x3f) << 16)
+ | ((mode->Flags & V_NHSYNC)
+ ? RADEON_CRTC_H_SYNC_POL
+ : 0));
+
+#if 1
+ /* This works for double scan mode. */
+ save->crtc_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
+ | ((mode->CrtcVDisplay - 1) << 16));
+#else
+ /* This is what cce/nbmode.c example code
+ * does -- is this correct?
+ */
+ save->crtc_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
+ | ((mode->CrtcVDisplay
+ * ((mode->Flags & V_DBLSCAN) ? 2 : 1) - 1)
+ << 16));
+#endif
+
+ vsync_wid = mode->CrtcVSyncEnd - mode->CrtcVSyncStart;
+ if (!vsync_wid) vsync_wid = 1;
+
+ save->crtc_v_sync_strt_wid = (((mode->CrtcVSyncStart - 1) & 0xfff)
+ | ((vsync_wid & 0x1f) << 16)
+ | ((mode->Flags & V_NVSYNC)
+ ? RADEON_CRTC_V_SYNC_POL
+ : 0));
+
+ save->crtc_offset = 0;
+ save->crtc_offset_cntl = INREG(RADEON_CRTC_OFFSET_CNTL);
+
+ save->crtc_pitch = (((pScrn->displayWidth * pScrn->bitsPerPixel) +
+ ((pScrn->bitsPerPixel * 8) -1)) /
+ (pScrn->bitsPerPixel * 8));
+ save->crtc_pitch |= save->crtc_pitch << 16;
+
+ save->surface_cntl = 0;
+
+#if X_BYTE_ORDER == X_BIG_ENDIAN
+ switch (pScrn->bitsPerPixel) {
+ case 16:
+ save->surface_cntl |= RADEON_NONSURF_AP0_SWP_16BPP;
+ break;
+
+ case 32:
+ save->surface_cntl |= RADEON_NONSURF_AP0_SWP_32BPP;
+ break;
+ }
+#endif
+
+ RADEONTRACE(("Pitch = %d bytes (virtualX = %d, displayWidth = %d)\n",
+ save->crtc_pitch, pScrn->virtualX,
+ info->CurrentLayout.displayWidth));
+ return TRUE;
+}
+
+/* Define CRTC2 registers for requested video mode */
+static Bool RADEONInitCrtc2Registers(ScrnInfoPtr pScrn, RADEONSavePtr save,
+ DisplayModePtr mode, RADEONInfoPtr info)
+{
+ unsigned char *RADEONMMIO = info->MMIO;
+
+ int format;
+ int hsync_start;
+ int hsync_wid;
+ int hsync_fudge;
+ int vsync_wid;
+ int bytpp;
+ int hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
+
+ switch (info->CurrentLayout.pixel_code) {
+ case 4: format = 1; bytpp = 0; break;
+ case 8: format = 2; bytpp = 1; break;
+ case 15: format = 3; bytpp = 2; break; /* 555 */
+ case 16: format = 4; bytpp = 2; break; /* 565 */
+ case 24: format = 5; bytpp = 3; break; /* RGB */
+ case 32: format = 6; bytpp = 4; break; /* xRGB */
+ default:
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+ "Unsupported pixel depth (%d)\n",
+ info->CurrentLayout.bitsPerPixel);
+ return FALSE;
+ }
+ RADEONTRACE(("Format = %d (%d bytes per pixel)\n", format, bytpp));
+
+ hsync_fudge = hsync_fudge_default[format-1];
+
+ save->crtc2_gen_cntl = (RADEON_CRTC2_EN
+ | RADEON_CRTC2_CRT2_ON
+ | (format << 8)
+ | ((mode->Flags & V_DBLSCAN)
+ ? RADEON_CRTC2_DBL_SCAN_EN
+ : 0)
+ | ((mode->Flags & V_CSYNC)
+ ? RADEON_CRTC2_CSYNC_EN
+ : 0)
+ | ((mode->Flags & V_INTERLACE)
+ ? RADEON_CRTC2_INTERLACE_EN
+ : 0));
+
+ /* Turn CRT on in case the first head is a DFP */
+ save->crtc_ext_cntl |= RADEON_CRTC_CRT_ON;
+ save->dac2_cntl = info->SavedReg.dac2_cntl;
+ if (info->ChipFamily == CHIP_FAMILY_R200 ||
+ info->ChipFamily == CHIP_FAMILY_R300) {
+ save->disp_output_cntl =
+ ((info->SavedReg.disp_output_cntl
+ & ~(CARD32)RADEON_DISP_DAC_SOURCE_MASK)
+ | RADEON_DISP_DAC_SOURCE_CRTC2);
+ } else {
+ save->disp_hw_debug = info->SavedReg.disp_hw_debug;
+ if (info->IsDell && info->DellType == 2) {
+ if (info->DisplayType == MT_CRT || info->CloneType == MT_CRT) {
+ /* Turn on 2nd CRT */
+ save->dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL;
+ save->dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL;
+ save->disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL;
+
+ /* This will make 2nd CRT stay on in console */
+ info->SavedReg.dac2_cntl = save->dac2_cntl;
+ info->SavedReg.disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
+ info->SavedReg.crtc2_gen_cntl |= RADEON_CRTC2_CRT2_ON;
+ }
+ } else save->dac2_cntl |= RADEON_DAC2_DAC_CLK_SEL;
+ }
+
+ save->crtc2_h_total_disp =
+ ((((mode->CrtcHTotal / 8) - 1) & 0x3ff)
+ | ((((mode->CrtcHDisplay / 8) - 1) & 0x1ff) << 16));
+
+ hsync_wid = (mode->CrtcHSyncEnd - mode->CrtcHSyncStart) / 8;
+ if (!hsync_wid) hsync_wid = 1;
+ hsync_start = mode->CrtcHSyncStart - 8 + hsync_fudge;
+
+ save->crtc2_h_sync_strt_wid = ((hsync_start & 0x1fff)
+ | ((hsync_wid & 0x3f) << 16)
+ | ((mode->Flags & V_NHSYNC)
+ ? RADEON_CRTC_H_SYNC_POL
+ : 0));
+
+#if 1
+ /* This works for double scan mode. */
+ save->crtc2_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
+ | ((mode->CrtcVDisplay - 1) << 16));
+#else
+ /* This is what cce/nbmode.c example code
+ * does -- is this correct?
+ */
+ save->crtc2_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
+ | ((mode->CrtcVDisplay
+ * ((mode->Flags & V_DBLSCAN) ? 2 : 1) - 1)
+ << 16));
+#endif
+
+ vsync_wid = mode->CrtcVSyncEnd - mode->CrtcVSyncStart;
+ if (!vsync_wid) vsync_wid = 1;
+
+ save->crtc2_v_sync_strt_wid = (((mode->CrtcVSyncStart - 1) & 0xfff)
+ | ((vsync_wid & 0x1f) << 16)
+ | ((mode->Flags & V_NVSYNC)
+ ? RADEON_CRTC2_V_SYNC_POL
+ : 0));
+
+ save->crtc2_offset = 0;
+ save->crtc2_offset_cntl = INREG(RADEON_CRTC2_OFFSET_CNTL);
+
+ save->crtc2_pitch = (((pScrn->displayWidth * pScrn->bitsPerPixel) +
+ ((pScrn->bitsPerPixel * 8) -1)) /
+ (pScrn->bitsPerPixel * 8));
+ save->crtc2_pitch |= save->crtc2_pitch << 16;
+
+ if (info->DisplayType == MT_DFP || info->CloneType == MT_DFP) {
+ save->crtc2_gen_cntl = (RADEON_CRTC2_EN | (format << 8));
+ save->fp2_h_sync_strt_wid = save->crtc2_h_sync_strt_wid;
+ save->fp2_v_sync_strt_wid = save->crtc2_v_sync_strt_wid;
+ save->fp2_gen_cntl = (RADEON_FP2_SEL_CRTC2 |
+ RADEON_FP2_PANEL_FORMAT |
+ RADEON_FP2_ON);
+
+ if (pScrn->rgbBits == 8)
+ save->fp2_gen_cntl |= RADEON_FP2_PANEL_FORMAT; /* 24 bit format */
+ else
+ save->fp2_gen_cntl &= ~RADEON_FP2_PANEL_FORMAT;/* 18 bit format */
+
+ /* FIXME: When there are two DFPs, the 2nd DFP is driven by the
+ * external TMDS transmitter. It may have a problem at
+ * high dot clock for certain panels. Since we don't
+ * know how to control the external TMDS transmitter, not
+ * much we can do here.
+ */
+#if 0
+ if (save->dot_clock_freq > 15000)
+ save->tmds_pll_cntl = 0xA3F;
+ else if(save->tmds_pll_cntl != 0xA3F)
+ save->tmds_pll_cntl = info->SavedReg.tmds_pll_cntl;
+#endif
+
+ /* If BIOS has not turned it on, we'll keep it on so that we'll
+ * have a valid VGA screen even after X quits or VT is switched
+ * to the console mode.
+ */
+ info->SavedReg.fp2_gen_cntl = RADEON_FP2_ON;
+ }
+
+ RADEONTRACE(("Pitch = %d bytes (virtualX = %d, displayWidth = %d)\n",
+ save->crtc2_pitch, pScrn->virtualX,
+ info->CurrentLayout.displayWidth));
+
+ return TRUE;
+}
+
+/* Define CRTC registers for requested video mode */
+static void RADEONInitFPRegisters(ScrnInfoPtr pScrn, RADEONSavePtr orig,
+ RADEONSavePtr save, DisplayModePtr mode,
+ RADEONInfoPtr info)
+{
+ int xres = mode->HDisplay;
+ int yres = mode->VDisplay;
+ float Hratio, Vratio;
+
+ if (info->PanelXRes == 0 || info->PanelYRes == 0) {
+ Hratio = 1.0;
+ Vratio = 1.0;
+ } else {
+ if (xres > info->PanelXRes) xres = info->PanelXRes;
+ if (yres > info->PanelYRes) yres = info->PanelYRes;
+
+ Hratio = (float)xres/(float)info->PanelXRes;
+ Vratio = (float)yres/(float)info->PanelYRes;
+ }
+
+ if (Hratio == 1.0 || !(mode->Flags & RADEON_USE_RMX)) {
+ save->fp_horz_stretch = orig->fp_horz_stretch;
+ save->fp_horz_stretch &= ~(RADEON_HORZ_STRETCH_BLEND |
+ RADEON_HORZ_STRETCH_ENABLE);
+ save->fp_horz_stretch &= ~(RADEON_HORZ_AUTO_RATIO |
+ RADEON_HORZ_PANEL_SIZE);
+ save->fp_horz_stretch |= ((xres/8-1)<<16);
+
+ } else {
+ save->fp_horz_stretch =
+ ((((unsigned long)(Hratio * RADEON_HORZ_STRETCH_RATIO_MAX +
+ 0.5)) & RADEON_HORZ_STRETCH_RATIO_MASK)) |
+ (orig->fp_horz_stretch & (RADEON_HORZ_PANEL_SIZE |
+ RADEON_HORZ_FP_LOOP_STRETCH |
+ RADEON_HORZ_AUTO_RATIO_INC));
+ save->fp_horz_stretch |= (RADEON_HORZ_STRETCH_BLEND |
+ RADEON_HORZ_STRETCH_ENABLE);
+
+ save->fp_horz_stretch &= ~(RADEON_HORZ_AUTO_RATIO |
+ RADEON_HORZ_PANEL_SIZE);
+ save->fp_horz_stretch |= ((info->PanelXRes / 8 - 1) << 16);
+
+ }
+
+ if (Vratio == 1.0 || !(mode->Flags & RADEON_USE_RMX)) {
+ save->fp_vert_stretch = orig->fp_vert_stretch;
+ save->fp_vert_stretch &= ~(RADEON_VERT_STRETCH_ENABLE|
+ RADEON_VERT_STRETCH_BLEND);
+ save->fp_vert_stretch &= ~(RADEON_VERT_AUTO_RATIO_EN |
+ RADEON_VERT_PANEL_SIZE);
+ save->fp_vert_stretch |= ((yres-1) << 12);
+ } else {
+ save->fp_vert_stretch =
+ (((((unsigned long)(Vratio * RADEON_VERT_STRETCH_RATIO_MAX +
+ 0.5)) & RADEON_VERT_STRETCH_RATIO_MASK)) |
+ (orig->fp_vert_stretch & (RADEON_VERT_PANEL_SIZE |
+ RADEON_VERT_STRETCH_RESERVED)));
+ save->fp_vert_stretch |= (RADEON_VERT_STRETCH_ENABLE |
+ RADEON_VERT_STRETCH_BLEND);
+
+ save->fp_vert_stretch &= ~(RADEON_VERT_AUTO_RATIO_EN |
+ RADEON_VERT_PANEL_SIZE);
+ save->fp_vert_stretch |= ((info->PanelYRes-1) << 12);
+
+ }
+
+ save->fp_gen_cntl = (orig->fp_gen_cntl & (CARD32)
+ ~(RADEON_FP_SEL_CRTC2 |
+ RADEON_FP_RMX_HVSYNC_CONTROL_EN |
+ RADEON_FP_DFP_SYNC_SEL |
+ RADEON_FP_CRT_SYNC_SEL |
+ RADEON_FP_CRTC_LOCK_8DOT |
+ RADEON_FP_USE_SHADOW_EN |
+ RADEON_FP_CRTC_USE_SHADOW_VEND |
+ RADEON_FP_CRT_SYNC_ALT));
+ save->fp_gen_cntl |= (RADEON_FP_CRTC_DONT_SHADOW_VPAR |
+ RADEON_FP_CRTC_DONT_SHADOW_HEND );
+
+ if (pScrn->rgbBits == 8)
+ save->fp_gen_cntl |= RADEON_FP_PANEL_FORMAT; /* 24 bit format */
+ else
+ save->fp_gen_cntl &= ~RADEON_FP_PANEL_FORMAT;/* 18 bit format */
+
+ save->lvds_gen_cntl = orig->lvds_gen_cntl;
+ save->lvds_pll_cntl = orig->lvds_pll_cntl;
+
+ /* This is needed for some panel at high resolution (>=1600x1200)
+ */
+ if ((save->dot_clock_freq > 15000) &&
+ (info->ChipFamily != CHIP_FAMILY_R300))
+ save->tmds_pll_cntl = 0xA3F;
+ else
+ save->tmds_pll_cntl = orig->tmds_pll_cntl;
+
+ info->PanelOff = FALSE;
+ /* This option is used to force the ONLY DEVICE in XFConfig to use
+ * CRT port, instead of default DVI port.
+ */
+ if (xf86ReturnOptValBool(info->Options, OPTION_PANEL_OFF, FALSE)) {
+ info->PanelOff = TRUE;
+ }
+
+ if (info->PanelOff && info->Clone) {
+ info->OverlayOnCRTC2 = TRUE;
+ if (info->DisplayType == MT_LCD) {
+ /* Turning off LVDS_ON seems to make panel white blooming.
+ * For now we just turn off display data ???
+ */
+ save->lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_DISPLAY_DIS);
+ save->lvds_gen_cntl &= ~(RADEON_LVDS_BLON);
+
+ } else if (info->DisplayType == MT_DFP)
+ save->fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN);
+ } else {
+ if (info->DisplayType == MT_LCD) {
+ save->lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_BLON);
+ save->fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN);
+ } else if (info->DisplayType == MT_DFP)
+ save->fp_gen_cntl |= (RADEON_FP_FPON | RADEON_FP_TMDS_EN);
+ }
+
+ save->fp_crtc_h_total_disp = save->crtc_h_total_disp;
+ save->fp_crtc_v_total_disp = save->crtc_v_total_disp;
+ save->fp_h_sync_strt_wid = save->crtc_h_sync_strt_wid;
+ save->fp_v_sync_strt_wid = save->crtc_v_sync_strt_wid;
+}
+
+/* Define PLL registers for requested video mode */
+static void RADEONInitPLLRegisters(RADEONSavePtr save, RADEONPLLPtr pll,
+ double dot_clock)
+{
+ unsigned long freq = dot_clock * 100;
+
+ struct {
+ int divider;
+ int bitvalue;
+ } *post_div, post_divs[] = {
+ /* From RAGE 128 VR/RAGE 128 GL Register
+ * Reference Manual (Technical Reference
+ * Manual P/N RRG-G04100-C Rev. 0.04), page
+ * 3-17 (PLL_DIV_[3:0]).
+ */
+ { 1, 0 }, /* VCLK_SRC */
+ { 2, 1 }, /* VCLK_SRC/2 */
+ { 4, 2 }, /* VCLK_SRC/4 */
+ { 8, 3 }, /* VCLK_SRC/8 */
+ { 3, 4 }, /* VCLK_SRC/3 */
+ { 16, 5 }, /* VCLK_SRC/16 */
+ { 6, 6 }, /* VCLK_SRC/6 */
+ { 12, 7 }, /* VCLK_SRC/12 */
+ { 0, 0 }
+ };
+
+ if (freq > pll->max_pll_freq) freq = pll->max_pll_freq;
+ if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12;
+
+ for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
+ save->pll_output_freq = post_div->divider * freq;
+ if (save->pll_output_freq >= pll->min_pll_freq
+ && save->pll_output_freq <= pll->max_pll_freq) break;
+ }
+
+ save->dot_clock_freq = freq;
+ save->feedback_div = RADEONDiv(pll->reference_div
+ * save->pll_output_freq,
+ pll->reference_freq);
+ save->post_div = post_div->divider;
+
+ RADEONTRACE(("dc=%d, of=%d, fd=%d, pd=%d\n",
+ save->dot_clock_freq,
+ save->pll_output_freq,
+ save->feedback_div,
+ save->post_div));
+
+ save->ppll_ref_div = pll->reference_div;
+ save->ppll_div_3 = (save->feedback_div | (post_div->bitvalue << 16));
+ save->htotal_cntl = 0;
+}
+
+/* Define PLL2 registers for requested video mode */
+static void RADEONInitPLL2Registers(RADEONSavePtr save, RADEONPLLPtr pll,
+ double dot_clock)
+{
+ unsigned long freq = dot_clock * 100;
+
+ struct {
+ int divider;
+ int bitvalue;
+ } *post_div, post_divs[] = {
+ /* From RAGE 128 VR/RAGE 128 GL Register
+ * Reference Manual (Technical Reference
+ * Manual P/N RRG-G04100-C Rev. 0.04), page
+ * 3-17 (PLL_DIV_[3:0]).
+ */
+ { 1, 0 }, /* VCLK_SRC */
+ { 2, 1 }, /* VCLK_SRC/2 */
+ { 4, 2 }, /* VCLK_SRC/4 */
+ { 8, 3 }, /* VCLK_SRC/8 */
+ { 3, 4 }, /* VCLK_SRC/3 */
+ { 6, 6 }, /* VCLK_SRC/6 */
+ { 12, 7 }, /* VCLK_SRC/12 */
+ { 0, 0 }
+ };
+
+ if (freq > pll->max_pll_freq) freq = pll->max_pll_freq;
+ if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12;
+
+ for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
+ save->pll_output_freq_2 = post_div->divider * freq;
+ if (save->pll_output_freq_2 >= pll->min_pll_freq
+ && save->pll_output_freq_2 <= pll->max_pll_freq) break;
+ }
+
+ save->dot_clock_freq_2 = freq;
+ save->feedback_div_2 = RADEONDiv(pll->reference_div
+ * save->pll_output_freq_2,
+ pll->reference_freq);
+ save->post_div_2 = post_div->divider;
+
+ RADEONTRACE(("dc=%d, of=%d, fd=%d, pd=%d\n",
+ save->dot_clock_freq_2,
+ save->pll_output_freq_2,
+ save->feedback_div_2,
+ save->post_div_2));
+
+ save->p2pll_ref_div = pll->reference_div;
+ save->p2pll_div_0 = (save->feedback_div_2 |
+ (post_div->bitvalue << 16));
+ save->htotal_cntl2 = 0;
+}
+
+#if 0
+/* Define initial palette for requested video mode. This doesn't do
+ * anything for XFree86 4.0.
+ */
+static void RADEONInitPalette(RADEONSavePtr save)
+{
+ save->palette_valid = FALSE;
+}
+#endif
+
+/* Define registers for a requested video mode */
+static Bool RADEONInit(ScrnInfoPtr pScrn, DisplayModePtr mode,
+ RADEONSavePtr save)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ double dot_clock = mode->Clock/1000.0;
+
+#if RADEON_DEBUG
+ ErrorF("%-12.12s %7.2f %4d %4d %4d %4d %4d %4d %4d %4d (%d,%d)",
+ mode->name,
+ dot_clock,
+
+ mode->HDisplay,
+ mode->HSyncStart,
+ mode->HSyncEnd,
+ mode->HTotal,
+
+ mode->VDisplay,
+ mode->VSyncStart,
+ mode->VSyncEnd,
+ mode->VTotal,
+ pScrn->depth,
+ pScrn->bitsPerPixel);
+ if (mode->Flags & V_DBLSCAN) ErrorF(" D");
+ if (mode->Flags & V_CSYNC) ErrorF(" C");
+ if (mode->Flags & V_INTERLACE) ErrorF(" I");
+ if (mode->Flags & V_PHSYNC) ErrorF(" +H");
+ if (mode->Flags & V_NHSYNC) ErrorF(" -H");
+ if (mode->Flags & V_PVSYNC) ErrorF(" +V");
+ if (mode->Flags & V_NVSYNC) ErrorF(" -V");
+ ErrorF("\n");
+ ErrorF("%-12.12s %7.2f %4d %4d %4d %4d %4d %4d %4d %4d (%d,%d)",
+ mode->name,
+ dot_clock,
+
+ mode->CrtcHDisplay,
+ mode->CrtcHSyncStart,
+ mode->CrtcHSyncEnd,
+ mode->CrtcHTotal,
+
+ mode->CrtcVDisplay,
+ mode->CrtcVSyncStart,
+ mode->CrtcVSyncEnd,
+ mode->CrtcVTotal,
+ pScrn->depth,
+ pScrn->bitsPerPixel);
+ if (mode->Flags & V_DBLSCAN) ErrorF(" D");
+ if (mode->Flags & V_CSYNC) ErrorF(" C");
+ if (mode->Flags & V_INTERLACE) ErrorF(" I");
+ if (mode->Flags & V_PHSYNC) ErrorF(" +H");
+ if (mode->Flags & V_NHSYNC) ErrorF(" -H");
+ if (mode->Flags & V_PVSYNC) ErrorF(" +V");
+ if (mode->Flags & V_NVSYNC) ErrorF(" -V");
+ ErrorF("\n");
+#endif
+
+ info->Flags = mode->Flags;
+
+ if (info->IsSecondary) {
+ if (!RADEONInitCrtc2Registers(pScrn, save, mode, info))
+ return FALSE;
+ RADEONInitPLL2Registers(save, &info->pll, dot_clock);
+ } else {
+ RADEONInitCommonRegisters(save, info);
+ if (!RADEONInitCrtcRegisters(pScrn, save, mode, info))
+ return FALSE;
+ dot_clock = mode->Clock/1000.0;
+ if (dot_clock) {
+ RADEONInitPLLRegisters(save, &info->pll, dot_clock);
+ } else {
+ save->ppll_ref_div = info->SavedReg.ppll_ref_div;
+ save->ppll_div_3 = info->SavedReg.ppll_div_3;
+ save->htotal_cntl = info->SavedReg.htotal_cntl;
+ }
+
+ if (info->Clone && info->CurCloneMode) {
+ RADEONInitCrtc2Registers(pScrn, save, info->CurCloneMode, info);
+ dot_clock = info->CurCloneMode->Clock / 1000.0;
+ RADEONInitPLL2Registers(save, &info->pll, dot_clock);
+ }
+ /* Not used for now: */
+ /* if (!info->PaletteSavedOnVT) RADEONInitPalette(save); */
+ }
+
+ if (((info->DisplayType == MT_DFP) ||
+ (info->DisplayType == MT_LCD))) {
+ RADEONInitFPRegisters(pScrn, &info->SavedReg, save, mode, info);
+ }
+
+ RADEONTRACE(("RADEONInit returns %p\n", save));
+ return TRUE;
+}
+
+/* Initialize a new mode */
+static Bool RADEONModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+
+ if (!RADEONInit(pScrn, mode, &info->ModeReg)) return FALSE;
+
+ pScrn->vtSema = TRUE;
+ RADEONBlank(pScrn);
+ RADEONRestoreMode(pScrn, &info->ModeReg);
+ RADEONUnblank(pScrn);
+
+ info->CurrentLayout.mode = mode;
+ return TRUE;
+}
+
+static Bool RADEONSaveScreen(ScreenPtr pScreen, int mode)
+{
+ ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
+ Bool unblank;
+
+ unblank = xf86IsUnblank(mode);
+ if (unblank) SetTimeSinceLastInputEvent();
+
+ if ((pScrn != NULL) && pScrn->vtSema) {
+ if (unblank) RADEONUnblank(pScrn);
+ else RADEONBlank(pScrn);
+ }
+ return TRUE;
+}
+
+Bool RADEONSwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ Bool ret;
+#ifdef XF86DRI
+ Bool CPStarted = info->CPStarted;
+
+ if (CPStarted) {
+ DRILock(pScrn->pScreen, 0);
+ RADEONCP_STOP(pScrn, info);
+ }
+#endif
+
+ if (info->accelOn) info->accel->Sync(pScrn);
+
+ if (info->FBDev) {
+ RADEONSaveFBDevRegisters(pScrn, &info->ModeReg);
+
+ ret = fbdevHWSwitchMode(scrnIndex, mode, flags);
+
+ RADEONRestoreFBDevRegisters(pScrn, &info->ModeReg);
+ } else {
+ info->IsSwitching = TRUE;
+ if (info->Clone && info->CloneModes) {
+ DisplayModePtr clone_mode = info->CloneModes;
+
+ /* Try to match a mode on primary head
+ * FIXME: This may not be good if both heads don't have
+ * exactly the same list of mode.
+ */
+ while (1) {
+ if ((clone_mode->HDisplay == mode->HDisplay) &&
+ (clone_mode->VDisplay == mode->VDisplay) &&
+ (!info->PanelOff)) {
+ info->CloneFrameX0 = (info->CurCloneMode->HDisplay +
+ info->CloneFrameX0 -
+ clone_mode->HDisplay - 1) / 2;
+ info->CloneFrameY0 =
+ (info->CurCloneMode->VDisplay + info->CloneFrameY0 -
+ clone_mode->VDisplay - 1) / 2;
+ info->CurCloneMode = clone_mode;
+ break;
+ }
+
+ if (!clone_mode->next) {
+ info->CurCloneMode = info->CloneModes;
+ break;
+ }
+
+ clone_mode = clone_mode->next;
+ }
+ }
+ ret = RADEONModeInit(xf86Screens[scrnIndex], mode);
+
+ if (info->CurCloneMode) {
+ if (info->CloneFrameX0 + info->CurCloneMode->HDisplay >=
+ pScrn->virtualX)
+ info->CloneFrameX0 =
+ pScrn->virtualX - info->CurCloneMode->HDisplay;
+ else if (info->CloneFrameX0 < 0)
+ info->CloneFrameX0 = 0;
+
+ if (info->CloneFrameY0 + info->CurCloneMode->VDisplay >=
+ pScrn->virtualY)
+ info->CloneFrameY0 =
+ pScrn->virtualY - info->CurCloneMode->VDisplay;
+ else if (info->CloneFrameY0 < 0)
+ info->CloneFrameY0 = 0;
+
+ RADEONDoAdjustFrame(pScrn, info->CloneFrameX0, info->CloneFrameY0,
+ TRUE);
+ }
+
+ info->IsSwitching = FALSE;
+ }
+
+ if (info->accelOn) {
+ info->accel->Sync(pScrn);
+ RADEONEngineRestore(pScrn);
+ }
+
+#ifdef XF86DRI
+ if (CPStarted) {
+ RADEONCP_START(pScrn, info);
+ DRIUnlock(pScrn->pScreen);
+ }
+#endif
+
+ return ret;
+}
+
+/* Used to disallow modes that are not supported by the hardware */
+int RADEONValidMode(int scrnIndex, DisplayModePtr mode,
+ Bool verbose, int flag)
+{
+ /* Searching for native mode timing table embedded in BIOS image.
+ * Not working yet. Currently we calculate from FP registers
+ */
+
+ return MODE_OK;
+}
+
+/* Adjust viewport into virtual desktop such that (0,0) in viewport
+ * space is (x,y) in virtual space.
+ */
+void RADEONDoAdjustFrame(ScrnInfoPtr pScrn, int x, int y, int clone)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+ int reg, Base = y * info->CurrentLayout.displayWidth + x;
+#ifdef XF86DRI
+ RADEONSAREAPrivPtr pSAREAPriv;
+#endif
+
+ switch (info->CurrentLayout.pixel_code) {
+ case 15:
+ case 16: Base *= 2; break;
+ case 24: Base *= 3; break;
+ case 32: Base *= 4; break;
+ }
+
+ Base &= ~7; /* 3 lower bits are always 0 */
+
+ if (clone || info->IsSecondary) {
+ Base += pScrn->fbOffset;
+ reg = RADEON_CRTC2_OFFSET;
+ } else {
+ reg = RADEON_CRTC_OFFSET;
+ }
+
+#ifdef XF86DRI
+ if (info->directRenderingEnabled) {
+
+ pSAREAPriv = DRIGetSAREAPrivate(pScrn->pScreen);
+
+ if (pSAREAPriv->pfCurrentPage == 1) {
+ Base += info->backOffset;
+ }
+
+ if (clone || info->IsSecondary) {
+ pSAREAPriv->crtc2_base = Base;
+ }
+ }
+#endif
+
+ OUTREG(reg, Base);
+}
+
+void RADEONAdjustFrame(int scrnIndex, int x, int y, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+
+#ifdef XF86DRI
+ if (info->CPStarted) DRILock(pScrn->pScreen, 0);
+#endif
+
+ if (info->accelOn) info->accel->Sync(pScrn);
+
+ if (info->FBDev) {
+ fbdevHWAdjustFrame(scrnIndex, x, y, flags);
+ } else {
+ RADEONDoAdjustFrame(pScrn, x, y, FALSE);
+ }
+
+#ifdef XF86DRI
+ if (info->CPStarted) DRIUnlock(pScrn->pScreen);
+#endif
+}
+
+/* Called when VT switching back to the X server. Reinitialize the
+ * video mode.
+ */
+Bool RADEONEnterVT(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+
+ RADEONTRACE(("RADEONEnterVT\n"));
+
+ if (info->FBDev) {
+ unsigned char *RADEONMMIO = info->MMIO;
+ if (!fbdevHWEnterVT(scrnIndex,flags)) return FALSE;
+ info->PaletteSavedOnVT = FALSE;
+ info->ModeReg.surface_cntl = INREG(RADEON_SURFACE_CNTL);
+
+ RADEONRestoreFBDevRegisters(pScrn, &info->ModeReg);
+ } else
+ if (!RADEONModeInit(pScrn, pScrn->currentMode)) return FALSE;
+
+ if (info->accelOn)
+ RADEONEngineRestore(pScrn);
+
+#ifdef XF86DRI
+ if (info->directRenderingEnabled) {
+ RADEONCP_START(pScrn, info);
+ DRIUnlock(pScrn->pScreen);
+ }
+#endif
+
+ pScrn->AdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
+ if (info->CurCloneMode) {
+ RADEONDoAdjustFrame(pScrn, info->CloneFrameX0, info->CloneFrameY0, TRUE);
+ }
+
+ return TRUE;
+}
+
+/* Called when VT switching away from the X server. Restore the
+ * original text mode.
+ */
+void RADEONLeaveVT(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ RADEONSavePtr save = &info->ModeReg;
+
+ RADEONTRACE(("RADEONLeaveVT\n"));
+#ifdef XF86DRI
+ if (RADEONPTR(pScrn)->directRenderingEnabled) {
+ DRILock(pScrn->pScreen, 0);
+ RADEONCP_STOP(pScrn, info);
+ }
+#endif
+
+ if (info->FBDev) {
+ RADEONSavePalette(pScrn, save);
+ info->PaletteSavedOnVT = TRUE;
+
+ RADEONSaveFBDevRegisters(pScrn, &info->ModeReg);
+
+ fbdevHWLeaveVT(scrnIndex,flags);
+ }
+
+ RADEONRestore(pScrn);
+}
+
+/* Called at the end of each server generation. Restore the original
+ * text mode, unmap video memory, and unwrap and call the saved
+ * CloseScreen function.
+ */
+static Bool RADEONCloseScreen(int scrnIndex, ScreenPtr pScreen)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+
+ RADEONTRACE(("RADEONCloseScreen\n"));
+
+#ifdef XF86DRI
+ /* Disable direct rendering */
+ if (info->directRenderingEnabled) {
+ RADEONDRICloseScreen(pScreen);
+ info->directRenderingEnabled = FALSE;
+ }
+#endif
+
+ if (pScrn->vtSema) {
+ RADEONRestore(pScrn);
+ RADEONUnmapMem(pScrn);
+ }
+
+ if (info->accel) XAADestroyInfoRec(info->accel);
+ info->accel = NULL;
+
+ if (info->scratch_save) xfree(info->scratch_save);
+ info->scratch_save = NULL;
+
+ if (info->cursor) xf86DestroyCursorInfoRec(info->cursor);
+ info->cursor = NULL;
+
+ if (info->DGAModes) xfree(info->DGAModes);
+ info->DGAModes = NULL;
+
+ if (info->CloneModes)
+ while (info->CloneModes)
+ xf86DeleteMode(&info->CloneModes, info->CloneModes);
+
+ pScrn->vtSema = FALSE;
+
+ xf86ClearPrimInitDone(pScrn->entityList[0]);
+
+ pScreen->BlockHandler = info->BlockHandler;
+ pScreen->CloseScreen = info->CloseScreen;
+ return (*pScreen->CloseScreen)(scrnIndex, pScreen);
+}
+
+void RADEONFreeScreen(int scrnIndex, int flags)
+{
+ ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
+
+ RADEONTRACE(("RADEONFreeScreen\n"));
+
+ if (xf86LoaderCheckSymbol("vgaHWFreeHWRec"))
+ vgaHWFreeHWRec(pScrn);
+ RADEONFreeRec(pScrn);
+}
+
+/* Sets VESA Display Power Management Signaling (DPMS) Mode */
+static void RADEONDisplayPowerManagementSet(ScrnInfoPtr pScrn,
+ int PowerManagementMode,
+ int flags)
+{
+ RADEONInfoPtr info = RADEONPTR(pScrn);
+ unsigned char *RADEONMMIO = info->MMIO;
+
+#ifdef XF86DRI
+ if (info->CPStarted) DRILock(pScrn->pScreen, 0);
+#endif
+
+ if (info->accelOn) info->accel->Sync(pScrn);
+
+ if (info->FBDev) {
+ fbdevHWDPMSSet(pScrn, PowerManagementMode, flags);
+ } else {
+ int mask1 = (RADEON_CRTC_DISPLAY_DIS |
+ RADEON_CRTC_HSYNC_DIS |
+ RADEON_CRTC_VSYNC_DIS);
+ int mask2 = (RADEON_CRTC2_DISP_DIS |
+ RADEON_CRTC2_VSYNC_DIS |
+ RADEON_CRTC2_HSYNC_DIS);
+
+ /* TODO: additional handling for LCD ? */
+
+ switch (PowerManagementMode) {
+ case DPMSModeOn:
+ /* Screen: On; HSync: On, VSync: On */
+ if (info->IsSecondary)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL, 0, ~mask2);
+ else {
+ if (info->Clone)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL, 0, ~mask2);
+ OUTREGP(RADEON_CRTC_EXT_CNTL, 0, ~mask1);
+ }
+ break;
+
+ case DPMSModeStandby:
+ /* Screen: Off; HSync: Off, VSync: On */
+ if (info->IsSecondary)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_HSYNC_DIS,
+ ~mask2);
+ else {
+ if (info->Clone)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_HSYNC_DIS,
+ ~mask2);
+ OUTREGP(RADEON_CRTC_EXT_CNTL,
+ RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_HSYNC_DIS,
+ ~mask1);
+ }
+ break;
+
+ case DPMSModeSuspend:
+ /* Screen: Off; HSync: On, VSync: Off */
+ if (info->IsSecondary)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS,
+ ~mask2);
+ else {
+ if (info->Clone)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL,
+ RADEON_CRTC2_DISP_DIS | RADEON_CRTC2_VSYNC_DIS,
+ ~mask2);
+ OUTREGP(RADEON_CRTC_EXT_CNTL,
+ RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS,
+ ~mask1);
+ }
+ break;
+
+ case DPMSModeOff:
+ /* Screen: Off; HSync: Off, VSync: Off */
+ if (info->IsSecondary)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL, mask2, ~mask2);
+ else {
+ if (info->Clone)
+ OUTREGP(RADEON_CRTC2_GEN_CNTL, mask2, ~mask2);
+ OUTREGP(RADEON_CRTC_EXT_CNTL, mask1, ~mask1);
+ }
+ break;
+ }
+ }
+
+#ifdef XF86DRI
+ if (info->CPStarted) DRIUnlock(pScrn->pScreen);
+#endif
+}