summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/fbdev/tdfxfb.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index 9a06cef75699..fad365c00628 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -71,6 +71,7 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
@@ -336,6 +337,83 @@ static u32 do_calc_pll(int freq, int *freq_out)
return (n << 8) | (m << 2) | k;
}
+/*
+ * Convert a pllctrl register value back to a frequency in kHz.
+ * Formula from 3dfx documentation.
+ */
+static u32 tdfx_pll_to_khz(u32 pll)
+{
+ return (14318 * (((pll >> 8) & 0xff) + 2) /
+ (((pll >> 2) & 0x3f) + 2)) >> (pll & 3);
+}
+
+/* Layout of the "OEM config" table in voodoo 3 BIOS */
+struct tdfx_bios_cfg {
+ __le32 pciinit0; /* 0x00 */
+ __le32 miscinit0; /* 0x04 */
+ __le32 miscinit1; /* 0x08 */
+ __le32 draminit0; /* 0x0c */
+ __le32 draminit1; /* 0x10 */
+ __le32 agpinit0; /* 0x14 */
+ __le32 pllctrl1; /* 0x18 - memory PLL */
+ __le32 pllctrl2; /* 0x1c - graphics PLL */
+ __le32 sgrammode; /* 0x20 - SGRAM/SDRAM mode register data */
+} __packed;
+
+#define TDFX_ROM_CFG_PTR 0x50
+
+static bool tdfxfb_get_bios_cfg(struct pci_dev *pdev,
+ struct tdfx_bios_cfg *cfg)
+{
+ u16 romcfg, oemcfg;
+ void __iomem *rom;
+ size_t romsize;
+ u8 *image;
+ u32 khz;
+
+ /* This only works for the Voodoo 3 for now */
+ if (pdev->device != PCI_DEVICE_ID_3DFX_VOODOO3)
+ return false;
+
+ rom = pci_map_rom(pdev, &romsize);
+ if (!rom || !romsize)
+ return false;
+
+ image = vmalloc(romsize);
+ if (!image) {
+ pci_unmap_rom(pdev, rom);
+ return false;
+ }
+ memcpy_fromio(image, rom, romsize);
+ pci_unmap_rom(pdev, rom);
+
+ /* ROM[0x50] -> ROM config table -> OEM config table */
+ if (TDFX_ROM_CFG_PTR + 2 > romsize)
+ goto out;
+ romcfg = image[TDFX_ROM_CFG_PTR] | image[TDFX_ROM_CFG_PTR + 1] << 8;
+ if (romcfg == 0xffff || romcfg + 2 > romsize)
+ goto out;
+ oemcfg = image[romcfg] | image[romcfg + 1] << 8;
+ if (oemcfg == 0xffff || oemcfg + sizeof(*cfg) > romsize)
+ goto out;
+ memcpy(cfg, image + oemcfg, sizeof(*cfg));
+ vfree(image);
+
+ /*
+ * Make sure we didn't read garbage from the BIOS and will
+ * end up setting a frequency that explodes someone's expensive
+ * card.
+ */
+ khz = tdfx_pll_to_khz(le32_to_cpu(cfg->pllctrl1));
+ if (khz < 40000 || khz > 250000 || !le32_to_cpu(cfg->draminit0))
+ return false;
+ return true;
+
+out:
+ vfree(image);
+ return false;
+}
+
static void do_write_regs(struct fb_info *info, struct banshee_reg *reg)
{
struct tdfx_par *par = info->par;