summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorDaniel Palmer <daniel@0x0f.com>2026-07-31 21:19:10 +0900
committerHelge Deller <deller@gmx.de>2026-08-10 08:13:26 +0200
commitff21ab01014cd8625761a98d2ee09c55e9bbd0ea (patch)
tree2af13cb16264f2d927f8cf41dcadc3447ce264e5 /drivers/video/fbdev
parent27b8e3c27d858156c6a6b94919d5964fabd40f7a (diff)
downloadlinux-ff21ab01014cd8625761a98d2ee09c55e9bbd0ea.tar.gz
linux-ff21ab01014cd8625761a98d2ee09c55e9bbd0ea.zip
fbdev: tdfxfb: Add helper to read config table from BIOS
In the case that the video BIOS didn't run because the card isn't the primary card, the BIOS doesn't support running old skool video BIOS (modern BIOS without CSM), or the machine isn't x86 it needs to be booted manually. To do this the config table in the BIOS is needed. Add a helper to get the config table in preparation for manually booting cards. Signed-off-by: Daniel Palmer <daniel@0x0f.com> Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'drivers/video/fbdev')
-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;