/* * Copyright 2006-2009 Luc Verhaegen. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sub license, * 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 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * This file handles everything to do with the Host Bridge. * Things like FB sizing, FB memory typing and direct access are handled here. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "via_driver.h" #include "via_id.h" /* * Wrap around libpciaccess. */ #ifdef XSERVER_LIBPCIACCESS #define PCIDEVVAR struct pci_device * /* * Would it have been that hard to provide something for this? */ static struct pci_device * pci_device_from_ids_first(CARD16 Vendor, CARD16 Device) { struct pci_device *dev; struct pci_id_match match; struct pci_device_iterator *iter; match.vendor_id = Vendor; match.device_id = Device; match.subvendor_id = PCI_MATCH_ANY; match.subdevice_id = PCI_MATCH_ANY; match.device_class = 0; match.device_class_mask = 0; iter = pci_id_match_iterator_create(&match); dev = pci_device_next(iter); pci_iterator_destroy(iter); return dev; } #define PCIDEVFROMIDS(vendor, device) pci_device_from_ids_first((vendor), (device)) #define PCIDEVFROMPOS(bus, slot, func) pci_device_find_by_slot(0, (bus), (slot), (func)) #define PCIDEVFOUND(Dev) (Dev) #define PCIREADBYTE(pcidev, offset, data) pci_device_cfg_read_u8((pcidev), (data), (offset)) #define PCIREADWORD(pcidev, offset, data) pci_device_cfg_read_u16((pcidev), (data), (offset)) #define PCIWRITEWORD(pcidev, offset, data) pci_device_cfg_write_u16((pcidev), (data), (offset)) #else /* XSERVER_LIBPCIACCESS */ #define PCIDEVVAR PCITAG #define PCIDEVFROMIDS(vendor, device) pciFindFirst(((device) << 16) | (vendor), 0xFFFFFFFF) #define PCIDEVFROMPOS(bus, slot, func) pciTag((bus), (slot), (func)) #define PCIDEVFOUND(Dev) ((Dev) != PCI_NOT_FOUND) #define PCIREADBYTE(pcidev, offset, data) *(data) = pciReadByte((pcidev), (offset)) #define PCIREADWORD(pcidev, offset, data) *(data) = pciReadWord((pcidev), (offset)) #define PCIWRITEWORD(pcidev, offset, data) pciWriteWord((pcidev), (offset), (data)) #endif /* XSERVER_LIBPCIACCESS */ /* * Get the northbridge type. */ void ViaHostIdentify(ScrnInfoPtr pScrn) { struct { CARD16 ID; CARD8 Host; char *Name; } Hosts[] = { {0x3123, VIA_HOST_CLE266, "CLE266"}, {0x3205, VIA_HOST_KM400 , "KM400/KN400"}, {0x0296, VIA_HOST_P4M800, "P4M800"}, {0x0204, VIA_HOST_K8M800, "K8M800/K8N800"}, {0x0259, VIA_HOST_CN400, "CN400/PM800/PM880"}, {0x0314, VIA_HOST_CN700, "CN700/VN800/P4M800CE/P4M800Pro"}, {0x0324, VIA_HOST_CX700, "CX700/VX700"}, {0x0336, VIA_HOST_K8M890, "K8M890"}, {0x0327, VIA_HOST_P4M890, "P4M890"}, {0x0364, VIA_HOST_P4M900, "P4M900/VN896/CN896"}, {0x0353, VIA_HOST_VX800, "VX800"}, {0xFFFF, 0x00, NULL} }; VIAPtr pVia = VIAPTR(pScrn); PCIDEVVAR Dev = PCIDEVFROMPOS(0, 0, 0); CARD16 ID; int i; VIAFUNC(pScrn); PCIREADWORD(Dev, 0x02, &ID); for (i = 0; Hosts[i].Name; i++) if (Hosts[i].ID == ID) { pVia->Host = Hosts[i].Host; PCIREADBYTE(Dev, 0xF6, &pVia->HostRev); xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Found %s HostBridge (rev. 0x%02X).\n", Hosts[i].Name, pVia->HostRev); return; } xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unable to identify HostBridge\n", __func__); pVia->Host = VIA_HOST_UNKNOWN; } /* * Talk to the host bridge directly to get RAM timing for bandwidth checking. * */ static CARD8 CLE266RAMTypeGet(ScrnInfoPtr pScrn) { PCIDEVVAR Dev = PCIDEVFROMPOS(0, 0, 0); CARD8 Reg54, Reg60, Reg69, RegE3; int freq = 0; VIAFUNC(pScrn); PCIREADBYTE(Dev, 0x54, &Reg54); PCIREADBYTE(Dev, 0x69, &Reg69); switch (Reg54 >> 6) { /* FSB frequency */ case 0x01: /* 100Mhz */ switch (Reg69 >> 6) { case 0x00: freq = 100; break; case 0x01: freq = 133; break; case 0x02: freq = 66; break; default: /* Reserved */ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal FSB/DRAM frequency.\n", __func__); return VIA_MEM_NONE; } break; case 0x02: /* 133Mhz */ case 0x03: switch (Reg69 >> 6) { case 0x00: freq = 133; break; case 0x02: freq = 100; break; default: /* Reserved or excessive 166Mhz */ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal FSB/DRAM frequency.\n", __func__); return VIA_MEM_NONE; } break; default: /* Reserved */ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal FSB frequency.\n", __func__); break; } PCIREADBYTE(Dev, 0x60, &Reg60); PCIREADBYTE(Dev, 0xE3, &RegE3); if (RegE3 & 0x02) /* FB is on banks 2/3 */ Reg60 >>= 2; switch (Reg60 & 0x03) { /* Memory type */ case 0x00: /* SDR */ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: SDR memory is not handled.\n", __func__); return VIA_MEM_NONE; case 0x02: /* DDR */ switch (freq) { case 100: return VIA_MEM_DDR_200; case 133: return VIA_MEM_DDR_266; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled RAM frequency: %dMhz.\n", __func__, freq); return VIA_MEM_NONE; } default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal RAM type.\n", __func__); return VIA_MEM_NONE; } } /* * Values come from the BIOS. The FSB value was easy to distinguish. * How the FSB->RAM relation works out is not that clear, especially * not on KM400A. */ static CARD8 KM400RAMTypeGet(ScrnInfoPtr pScrn) { VIAPtr pVia = VIAPTR(pScrn); PCIDEVVAR Dev = PCIDEVFROMPOS(0, 0, 0); CARD8 FSB, RAM, tmp; VIAFUNC(pScrn); /* FSB frequency */ PCIREADBYTE(Dev, 0x54, &FSB); FSB >>= 6; /* FSB vs DRAM Clock */ PCIREADBYTE(Dev, 0x69, &RAM); RAM >>= 6; if (pVia->HostRev < 0x80) { /* KM400 */ switch (FSB) { /* FSB Clock */ case 0x00: switch (RAM) { case 0x00: return VIA_MEM_DDR_200; case 0x01: return VIA_MEM_DDR_266; case 0x02: return VIA_MEM_DDR_400; case 0x03: return VIA_MEM_DDR_333; } break; case 0x01: switch (RAM) { case 0x00: return VIA_MEM_DDR_266; case 0x01: return VIA_MEM_DDR_333; case 0x02: return VIA_MEM_DDR_400; } break; case 0x02: case 0x03: /* No 200Mhz FSB on KM400 */ switch (RAM) { case 0x00: return VIA_MEM_DDR_333; case 0x02: return VIA_MEM_DDR_400; case 0x03: return VIA_MEM_DDR_266; } break; } } else { /* KM400A */ PCIREADBYTE(Dev, 0x67, &tmp); if (tmp & 0x80) /* Beats me */ RAM |= 0x04; switch (FSB) { case 0x00: switch (RAM) { case 0x00: return VIA_MEM_DDR_200; case 0x01: return VIA_MEM_DDR_266; case 0x03: return VIA_MEM_DDR_333; case 0x07: return VIA_MEM_DDR_400; } break; case 0x01: switch (RAM) { case 0x00: return VIA_MEM_DDR_266; case 0x01: return VIA_MEM_DDR_333; case 0x03: return VIA_MEM_DDR_400; } break; case 0x02: switch (RAM) { case 0x00: return VIA_MEM_DDR_400; case 0x04: return VIA_MEM_DDR_333; case 0x06: return VIA_MEM_DDR_266; } break; case 0x03: switch (RAM) { case 0x00: return VIA_MEM_DDR_333; case 0x01: return VIA_MEM_DDR_400; case 0x04: return VIA_MEM_DDR_266; } break; } } xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal RAM type: FSB %1d, RAM, %1d\n", __func__, FSB, RAM); return VIA_MEM_NONE; } /* * Values are again from the BIOS, but here it was easy to see how things * added up. */ static CARD8 P4M800RAMTypeGet(ScrnInfoPtr pScrn) { CARD8 FSB, FSBtoRAM; int Freq; /* 0x4296 */ PCIREADBYTE(PCIDEVFROMPOS(0, 0, 4), 0xF3, &FSB); /* VIA scratch area */ switch(FSB >> 5) { case 0: Freq = 3; /* x33Mhz = 100Mhz */ break; case 1: Freq = 4; /* 133Mhz */ break; case 3: Freq = 5; /* 166Mhz */ break; case 2: Freq = 6; /* 200Mhz */ break; case 4: Freq = 7; /* 233Mhz */ break; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled FSB: %d\n", __func__, FSB); return VIA_MEM_NONE; } /* FSB to RAM timing from 0x3296 */ PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0x68, &FSBtoRAM); FSBtoRAM &= 0x0F; /* This link was straightforward this time */ if (FSBtoRAM & 0x02) Freq -= FSBtoRAM >> 2; else { Freq += FSBtoRAM >> 2; if (FSBtoRAM & 0x01) Freq++; } switch (Freq) { case 0x03: return VIA_MEM_DDR_200; case 0x04: return VIA_MEM_DDR_266; case 0x05: return VIA_MEM_DDR_333; case 0x06: return VIA_MEM_DDR_400; default: break; } xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal RAM type: FSB %1d, FSBtoRAM, %1d\n", __func__, FSB, FSBtoRAM); return VIA_MEM_NONE; } /* * Similar to P4M800, but not quite. */ static CARD8 CN400RAMTypeGet(ScrnInfoPtr pScrn) { CARD8 FSB, FSBtoRAM; int Freq; /* 0x2259 */ PCIREADBYTE(PCIDEVFROMPOS(0, 0, 2), 0x54, &FSB); switch(FSB >> 5) { case 0: Freq = 3; /* x33Mhz = 100Mhz */ break; case 1: Freq = 4; /* 133Mhz */ break; case 3: Freq = 5; /* 166Mhz */ break; case 2: Freq = 6; /* 200Mhz */ break; case 4: Freq = 7; /* 233Mhz */ break; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled FSB: %d\n", __func__, FSB); return VIA_MEM_NONE; } /* FSB to RAM timing from 0x3259 */ PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0x68, &FSBtoRAM); FSBtoRAM &= 0x0F; if (FSBtoRAM & 0x01) Freq += 1 + (FSBtoRAM >> 2); else if (FSBtoRAM & 0x02) Freq -= 1 + (FSBtoRAM >> 2); switch (Freq) { case 0x03: return VIA_MEM_DDR_200; case 0x04: return VIA_MEM_DDR_266; case 0x05: return VIA_MEM_DDR_333; case 0x06: return VIA_MEM_DDR_400; default: break; } xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal RAM type: FSB %1d, FSBtoRAM, %1d\n", __func__, FSB, FSBtoRAM); return VIA_MEM_NONE; } /* * Suited for CN700/VX800/P4M800CE/P4M800Pro, P4M890, P4M900/CN896, VX800. * */ static CARD8 CN700RAMTypeGet(ScrnInfoPtr pScrn) { CARD8 tmp; VIAFUNC(pScrn); PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0x90, &tmp); switch(tmp & 0x07) { case 0x00: return VIA_MEM_DDR_200; case 0x01: return VIA_MEM_DDR_266; case 0x02: return VIA_MEM_DDR_333; case 0x03: return VIA_MEM_DDR_400; case 0x04: return VIA_MEM_DDR2_400; case 0x05: return VIA_MEM_DDR2_533; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled DRAM MemClk: 0x%02X.\n", __func__, tmp); return VIA_MEM_NONE; } } /* * From the different AMD CPU bios and kernel developers guides. * AMD is nice like that :) */ static CARD8 AMDRAMTypeGet(ScrnInfoPtr pScrn) { PCIDEVVAR Dev; CARD8 tmp; VIAFUNC(pScrn); /* AMD 0Fh DRAM Controller */ Dev = PCIDEVFROMIDS(0x1022, 0x1102); if (PCIDEVFOUND(Dev)) { /* first, find out whether this is an NPT one */ PCIDEVVAR MiscDev = PCIDEVFROMIDS(0x1022, 0x1103); /* get alternate cpuid */ PCIREADBYTE(MiscDev, 0xFD, &tmp); if (tmp) { /* Must be NPT */ PCIREADBYTE(Dev, 0x94, &tmp); switch (tmp & 0x03) { case 0x00: return VIA_MEM_DDR2_400; case 0x01: return VIA_MEM_DDR2_533; case 0x02: return VIA_MEM_DDR2_667; case 0x03: return VIA_MEM_DDR2_800; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled DRAM MemClk: 0x%02X.\n", __func__, tmp); return VIA_MEM_NONE; } } else { PCIREADBYTE(Dev, 0x96, &tmp); tmp >>= 4; tmp &= 0x07; switch(tmp) { case 0x00: return VIA_MEM_DDR_200; case 0x02: return VIA_MEM_DDR_266; case 0x05: return VIA_MEM_DDR_333; case 0x07: return VIA_MEM_DDR_400; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled DRAM MemClk: 0x%02X.\n", __func__, tmp); return VIA_MEM_NONE; } } } /* AMD 10h DRAM Controller */ Dev = PCIDEVFROMIDS(0x1022, 0x1202); if (PCIDEVFOUND(Dev)) { CARD8 type; PCIREADBYTE(Dev, 0x94, &tmp); PCIREADBYTE(Dev, 0x95, &type); if (type & 0x01) { /* DDR3 */ #ifndef NO_DDR3_YET xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: DDR3 is not supported yet.\n", __func__); return VIA_MEM_NONE; #else switch(tmp & 0x07) { case 0x03: return VIA_MEM_DDR3_800; case 0x04: return VIA_MEM_DDR3_1066; case 0x05: return VIA_MEM_DDR3_1333; case 0x06: return VIA_MEM_DDR3_1600; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled DRAM MemClk: 0x%02X.\n", __func__, tmp); return VIA_MEM_NONE; } #endif } else { /* DDR2 */ switch(tmp & 0x07) { case 0x00: return VIA_MEM_DDR2_400; case 0x01: return VIA_MEM_DDR2_533; case 0x02: return VIA_MEM_DDR2_667; case 0x03: return VIA_MEM_DDR2_800; case 0x04: return VIA_MEM_DDR2_1066; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled DRAM MemClk: 0x%02X.\n", __func__, tmp); return VIA_MEM_NONE; } } } /* AMD 11h DRAM Controller */ Dev = PCIDEVFROMIDS(0x1022, 0x1302); if (PCIDEVFOUND(Dev)) { PCIREADBYTE(Dev, 0x94, &tmp); switch(tmp & 0x07) { case 0x01: return VIA_MEM_DDR2_533; case 0x02: return VIA_MEM_DDR2_667; case 0x03: return VIA_MEM_DDR2_800; default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled DRAM MemClk: 0x%02X.\n", __func__, tmp); return VIA_MEM_NONE; } } xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unable to find an AMD CPU DRAM Controller.\n", __func__); return VIA_MEM_NONE; } /* * Suited for CX700. */ static CARD8 CX700RAMTypeGet(ScrnInfoPtr pScrn) { PCIDEVVAR Dev = PCIDEVFROMPOS(0, 0, 3); CARD8 Clock, Type; VIAFUNC(pScrn); PCIREADBYTE(Dev, 0x90, &Clock); /* DRAM Clock */ PCIREADBYTE(Dev, 0x6C, &Type); /* DDR or DDR2 */ Type &= 0x40; Type >>= 6; switch (Type) { case 0: /* DDR */ switch (Clock) { case 0: return VIA_MEM_DDR_200; case 1: return VIA_MEM_DDR_266; case 2: return VIA_MEM_DDR_333; case 3: return VIA_MEM_DDR_400; } break; case 1: /* DDR2 */ switch (Clock) { case 3: return VIA_MEM_DDR2_400; case 4: return VIA_MEM_DDR2_533; } break; } xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Illegal RAM type: Type %1d, Clock, %1d\n", __func__, Type, Clock); return VIA_MEM_NONE; } /* * */ static char * ViaMemoryInfoString(CARD8 MemType) { struct { CARD8 Index; char *Info; } MemoryInfo[VIA_MEM_END] = { { VIA_MEM_DDR_200, "100MHz DDR - PC-1600"}, { VIA_MEM_DDR_266, "133MHz DDR - PC-2100"}, { VIA_MEM_DDR_333, "166MHz DDR - PC-2700"}, { VIA_MEM_DDR_400, "200MHz DDR - PC-3200"}, { VIA_MEM_DDR2_400, "100MHz DDR2 - PC2-3200"}, { VIA_MEM_DDR2_533, "133MHz DDR2 - PC2-4200"}, { VIA_MEM_DDR2_667, "166MHz DDR2 - PC2-5300"}, { VIA_MEM_DDR2_800, "200MHz DDR2 - PC2-6400"}, { VIA_MEM_DDR2_1066, "266MHz DDR2 - PC2-8500"}, }; int i; for (i = 0; i < VIA_MEM_END; i++) if (MemoryInfo[i].Index == MemType) return MemoryInfo[i].Info; return "Unknown"; } /* * */ Bool ViaFBInit(ScrnInfoPtr pScrn) { VIAPtr pVia = VIAPTR(pScrn); CARD8 tmp; VIAFUNC(pScrn); pVia->MemType = VIA_MEM_NONE; switch (pVia->Host) { case VIA_HOST_CLE266: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 0), 0xE1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 1024; pVia->MemType = CLE266RAMTypeGet(pScrn); break; case VIA_HOST_KM400: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 0), 0xE1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 1024; pVia->MemType = KM400RAMTypeGet(pScrn); break; case VIA_HOST_P4M800: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 1024; pVia->MemType = P4M800RAMTypeGet(pScrn); break; case VIA_HOST_K8M800: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 1024; pVia->MemType = AMDRAMTypeGet(pScrn); break; case VIA_HOST_CN400: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 1024; pVia->MemType = CN400RAMTypeGet(pScrn); break; case VIA_HOST_CN700: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 1024; pVia->MemType = CN700RAMTypeGet(pScrn); break; case VIA_HOST_CX700: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 4096; pVia->MemType = CX700RAMTypeGet(pScrn); break; case VIA_HOST_K8M890: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 4096; pVia->MemType = AMDRAMTypeGet(pScrn); break; case VIA_HOST_P4M890: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 4096; pVia->MemType = CX700RAMTypeGet(pScrn); break; case VIA_HOST_P4M900: case VIA_HOST_VX800: PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0xA1, &tmp); pScrn->videoRam = (1 << ((tmp & 0x70) >> 4)) * 4096; pVia->MemType = CN700RAMTypeGet(pScrn); break; default: xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "%s: Unhandled HostBridge.\n", __func__); return FALSE; } if (pVia->MemType == VIA_MEM_NONE) pVia->MemType = pVia->Scratch->MemType; /* should be hostbridge specific and should be extended */ if (pVia->MemType == VIA_MEM_DDR_200) pVia->Bandwidth = 394000000; /* 1280x1024@75Hz */ else if (pVia->MemType < VIA_MEM_DDR2_533) pVia->Bandwidth = 461000000; /* 1600x1200@60Hz */ else pVia->Bandwidth = 553000000; /* 1920x1200@60Hz */ xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Using %dkB of RAM (%s)\n", pScrn->videoRam, ViaMemoryInfoString(pVia->MemType)); return TRUE; } /* * On recent AMD CPUs the RAM sits off the CPU. Unichromes are all IGPs, so * they use a part of this RAM for their framebuffer. This makes chipsets for * AMD cpus extremely slow and very latency prone. * * The Int15 0xE820 call to find out the RAM size (as used by the linux kernel) * simply returns (RAM Size - FB Size). If the linux kernel talked to the AMD * CPU directly, then it would wrongly map the FB as well. * * If we assume that the FB always sits at the high end of the RAM (like VIAs * code now does), we can map this directly, and have direct CPU access to the * FB. * * So now we can now shift in and out of system RAM at lighting speed, faster * than on all other IGPs. But then, this doesn't change the latency issues for * the graphics engines themselves. * * VIA's own RAM Controllers can intercept FB accesses from CPU, saving us * quite some latency and on-chip bandwidth. So on devices that are not using * and AMD K8, we also get some advantage here. */ /* * */ static Bool CLE266FBDirectAccess(int scrnIndex, unsigned long *FBBase, Bool Force, Bool ForceOn) { PCIDEVVAR Dev = PCIDEVFROMPOS(0, 0, 0); CARD16 tmp; PCIREADWORD(Dev, 0xE0, &tmp); if (Force) { if (ForceOn) { /* We have to enable direct Access */ tmp &= ~0x0FFF; tmp |= ((*FBBase) >> 20) | 0x01; PCIWRITEWORD(Dev, 0xE0, tmp); return TRUE; } else { /* Disable */ tmp &= ~0x0001; PCIWRITEWORD(Dev, 0xE0, tmp); return FALSE; } } else { if ((tmp & 0x0001) && (tmp & 0xFFE)) { *FBBase = (((unsigned long) tmp) & 0xFFE) << 20; return TRUE; } else return FALSE; } } /* * */ static Bool P4M800FBDirectAccess(int scrnIndex, unsigned long *FBBase, Bool Force, Bool ForceOn) { PCIDEVVAR Dev = PCIDEVFROMPOS(0, 0, 3); CARD16 tmp; PCIREADWORD(Dev, 0xA0, &tmp); if (Force) { if (ForceOn) { /* We have to enable direct Access */ tmp &= ~0x0FFF; tmp |= ((*FBBase) >> 20) | 0x01; PCIWRITEWORD(Dev, 0xA0, tmp); /* if we don't kill the machine right here right now then we can return :) */ return TRUE; } else { /* Disable */ tmp &= ~0x0001; PCIWRITEWORD(Dev, 0xA0, tmp); return FALSE; } } else { if ((tmp & 0x0001) && (tmp & 0xFFE)) { *FBBase = (((unsigned long) tmp) & 0xFFE) << 20; return TRUE; } else return FALSE; } } /* * */ static Bool K8M800FBDirectAccess(int scrnIndex, unsigned long *FBBase, Bool Force, Bool ForceOn) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; PCIDEVVAR Dev; unsigned long FBBaseNew; CARD8 tmp; if (Force && *FBBase) /* just don't touch the FBBase */ return FALSE; Dev = PCIDEVFROMPOS(0, 0, 3); PCIREADBYTE(PCIDEVFROMPOS(0, 0, 3), 0x47, &tmp); FBBaseNew = (((unsigned long) tmp) & 0xFF) << 24; if (FBBaseNew > (pScrn->videoRam << 10)) { *FBBase = FBBaseNew - (pScrn->videoRam << 10); return TRUE; } else { xf86DrvMsg(scrnIndex, X_WARNING, "%s: RamController has crap data: 0:0.3 0x47: %08X", __func__, tmp); return FALSE; } } /* * */ void ViaFBBaseGet(ScrnInfoPtr pScrn) { VIAPtr pVia = VIAPTR(pScrn); unsigned long FBBase; VIAFUNC(pScrn); /* PCI BAR */ #ifdef XSERVER_LIBPCIACCESS FBBase = pVia->PciInfo->regions[0].base_addr; #else FBBase = pVia->PciInfo->memBase[0]; #endif if (pVia->FBDirectOption) { if (pVia->FBDirect) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Forcing Direct FB Access On.\n"); else xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Forcing Direct FB Access Off.\n"); } /* See if we have some magic */ switch (pVia->Host) { case VIA_HOST_CLE266: case VIA_HOST_KM400: pVia->FBDirect = CLE266FBDirectAccess(pScrn->scrnIndex, &FBBase, pVia->FBDirectOption, pVia->FBDirect); break; case VIA_HOST_K8M800: pVia->FBDirect = K8M800FBDirectAccess(pScrn->scrnIndex, &FBBase, pVia->FBDirectOption, pVia->FBDirect); break; case VIA_HOST_P4M800: case VIA_HOST_CN400: case VIA_HOST_CN700: case VIA_HOST_CX700: case VIA_HOST_P4M890: case VIA_HOST_P4M900: case VIA_HOST_VX800: pVia->FBDirect = P4M800FBDirectAccess(pScrn->scrnIndex, &FBBase, pVia->FBDirectOption, pVia->FBDirect); break; case VIA_HOST_K8M890: /* i would need to get my hands on one first */ default: xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: Unhandled HostBridge.\n", __func__); pVia->FBDirect = FALSE; break; } pVia->FrameBufferBase = FBBase; if (pVia->FBDirect) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct FB Access Enabled.\n"); else xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct FB Access Disabled.\n"); }