* [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work
@ 2026-07-31 12:19 Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 1/5] fbdev: tdfxfb: Add helper to read config table from BIOS Daniel Palmer
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Daniel Palmer @ 2026-07-31 12:19 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, Daniel Palmer
Currently voodoo cards that weren't booted by the video BIOS aren't
usable because the hardware is never brought up.
This means if you happen to have a proper - non-x86 - computer,
another card is the primary or you have a new BIOS that cannot
run the video BIOS tdfxfb would probe but you will be staring at
"no signal detected".
This series implements manually booting cards using the
values from the video BIOS' own table. Currently this only works
for the voodoo 3.
Debugging was partially assisted by Claude Fable 5. I could not
work out why this would not work for months so I told it to
create a voodoo 3 emulation for QEMU to compare the BIOS against
my code. That made it obvious my init sequence matched the BIOS
but the VGA core wasn't running.
This has been tested on a real voodoo 3 in an x86-64 board and
an Amiga 4000 with a mediator PCI bridge.
I have some patches to add an interface for doing 3D rendering
that are not included in this series and 3D rendering is also
working so as far as I can tell everything is good.
v2:
- Fix up some poor 3am English mistakes in the cover letter.
- Limit this to the voodoo 3. The BIOS layout is different
for the 4/5 apparently.
- Fixed the valid sashiko complaints - mainly clearing the
framebuffer memory before fbcon might have bound, retaining
the current behaviour if we can't get the video BIOS to get
the config parameters.
- Tweaked the wording of some of the commit messages.
Daniel Palmer (5):
fbdev: tdfxfb: Add helper to read config table from BIOS
fbdev: tdfxfb: Attempt to detect if the card wasn't booted
fbdev: tdfxfb: Manually boot unbooted cards
fbdev: tdfxfb: Wake the VGA core before programming the CRTC
fbdev: tdfxfb: Program the initial video mode
drivers/video/fbdev/tdfxfb.c | 155 +++++++++++++++++++++++++++++++++++
1 file changed, 155 insertions(+)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/5] fbdev: tdfxfb: Add helper to read config table from BIOS
2026-07-31 12:19 [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Daniel Palmer
@ 2026-07-31 12:19 ` Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 2/5] fbdev: tdfxfb: Attempt to detect if the card wasn't booted Daniel Palmer
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Daniel Palmer @ 2026-07-31 12:19 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, Daniel Palmer
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>
---
drivers/video/fbdev/tdfxfb.c | 78 ++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index cc6a074f3165..e92658274411 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;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/5] fbdev: tdfxfb: Attempt to detect if the card wasn't booted
2026-07-31 12:19 [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 1/5] fbdev: tdfxfb: Add helper to read config table from BIOS Daniel Palmer
@ 2026-07-31 12:19 ` Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 3/5] fbdev: tdfxfb: Manually boot unbooted cards Daniel Palmer
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Daniel Palmer @ 2026-07-31 12:19 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, Daniel Palmer
Until now a card had to have been booted by its video BIOS
otherwise the driver would probe, create the fb etc but there
would be no output on the display.
There doesn't seem to be a documented way work out if the BIOS
ran or not. Checking if the values in registers match what is
in the config table in the BIOS seems to be the only option.
On my 16MB Voodoo 3 3000 checking the contents of the draminit0
register versus what is in the config table seems to be enough.
Signed-off-by: Daniel Palmer <daniel@0x0f.com>
---
drivers/video/fbdev/tdfxfb.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index e92658274411..465c46edf6e7 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -414,6 +414,34 @@ static bool tdfxfb_get_bios_cfg(struct pci_dev *pdev,
return false;
}
+/*
+ * Try to work out if the card was booted or not, just checks if
+ * one of the dram config registers matches what is in the config
+ * table if there is one.
+ */
+static int tdfxfb_hw_init(struct fb_info *info, struct pci_dev *pdev)
+{
+ struct tdfx_par *par = info->par;
+ struct tdfx_bios_cfg cfg;
+ bool have_cfg = tdfxfb_get_bios_cfg(pdev, &cfg);
+
+ /*
+ * Can't tell if the card is booted or not,
+ * also cannot boot it. Card might not function.
+ */
+ if (!have_cfg)
+ return 0;
+
+ /* Card is, probably, already configured. */
+ if (tdfx_inl(par, DRAMINIT0) == le32_to_cpu(cfg.draminit0))
+ return 0;
+
+ dev_err(&pdev->dev,
+ "Card hasn't booted and is unusable\n");
+
+ return -ENODEV;
+}
+
static void do_write_regs(struct fb_info *info, struct banshee_reg *reg)
{
struct tdfx_par *par = info->par;
@@ -1509,6 +1537,9 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_err_regbase;
}
+ if (tdfxfb_hw_init(info, pdev))
+ goto out_err_regbase;
+
info->fix.smem_start = pci_resource_start(pdev, 1);
info->fix.smem_len = do_lfb_size(default_par, pdev->device);
if (!info->fix.smem_len) {
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 3/5] fbdev: tdfxfb: Manually boot unbooted cards
2026-07-31 12:19 [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 1/5] fbdev: tdfxfb: Add helper to read config table from BIOS Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 2/5] fbdev: tdfxfb: Attempt to detect if the card wasn't booted Daniel Palmer
@ 2026-07-31 12:19 ` Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 4/5] fbdev: tdfxfb: Wake the VGA core before programming the CRTC Daniel Palmer
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Daniel Palmer @ 2026-07-31 12:19 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, Daniel Palmer
If the card is detected as being unbooted it isn't too difficult
to use the config table in its BIOS to fire it up so do it.
Signed-off-by: Daniel Palmer <daniel@0x0f.com>
---
drivers/video/fbdev/tdfxfb.c | 40 +++++++++++++++++++++++++++++++++---
1 file changed, 37 insertions(+), 3 deletions(-)
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index 465c46edf6e7..4d92ab5b5430 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -67,6 +67,7 @@
#include <linux/aperture.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
@@ -418,9 +419,13 @@ static bool tdfxfb_get_bios_cfg(struct pci_dev *pdev,
* Try to work out if the card was booted or not, just checks if
* one of the dram config registers matches what is in the config
* table if there is one.
+ *
+ * If we have a BIOS config table attempt to manually boot the
+ * card if needed.
*/
static int tdfxfb_hw_init(struct fb_info *info, struct pci_dev *pdev)
{
+ u32 mempll, gfxpll, draminit0, draminit1, miscinit1, dram_mode;
struct tdfx_par *par = info->par;
struct tdfx_bios_cfg cfg;
bool have_cfg = tdfxfb_get_bios_cfg(pdev, &cfg);
@@ -436,10 +441,39 @@ static int tdfxfb_hw_init(struct fb_info *info, struct pci_dev *pdev)
if (tdfx_inl(par, DRAMINIT0) == le32_to_cpu(cfg.draminit0))
return 0;
- dev_err(&pdev->dev,
- "Card hasn't booted and is unusable\n");
+ dev_info(&pdev->dev,
+ "Manually booting card using config table\n");
+
+ mempll = le32_to_cpu(cfg.pllctrl1);
+ gfxpll = le32_to_cpu(cfg.pllctrl2);
+ draminit0 = le32_to_cpu(cfg.draminit0);
+ draminit1 = le32_to_cpu(cfg.draminit1);
+ miscinit1 = le32_to_cpu(cfg.miscinit1);
+ dram_mode = le32_to_cpu(cfg.sgrammode);
+ tdfx_outl(par, PCIINIT0, le32_to_cpu(cfg.pciinit0));
+ tdfx_outl(par, AGPINIT, le32_to_cpu(cfg.agpinit0));
+
+ /* memory clock, and the graphics clock if the card wants one */
+ tdfx_outl(par, PLLCTRL1, mempll);
+ if (gfxpll)
+ tdfx_outl(par, PLLCTRL2, gfxpll);
+ /* flush posted writes */
+ tdfx_inl(par, PLLCTRL1);
+ /* PLL lock */
+ udelay(100);
+
+ tdfx_outl(par, MISCINIT1, miscinit1);
+ tdfx_outl(par, DRAMINIT0, draminit0);
+ tdfx_outl(par, DRAMINIT1, draminit1);
+
+ /* SDRAM/SGRAM wake up: load the mode register */
+ tdfx_outl(par, DRAMDATA, dram_mode);
+ tdfx_outl(par, DRAMCOMMAND, 0x10d);
+
+ tdfx_outl(par, LFBMEMORYCONFIG, 0x00001fff);
+ tdfx_outl(par, MISCINIT0, le32_to_cpu(cfg.miscinit0));
- return -ENODEV;
+ return 0;
}
static void do_write_regs(struct fb_info *info, struct banshee_reg *reg)
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 4/5] fbdev: tdfxfb: Wake the VGA core before programming the CRTC
2026-07-31 12:19 [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Daniel Palmer
` (2 preceding siblings ...)
2026-07-31 12:19 ` [PATCH v2 3/5] fbdev: tdfxfb: Manually boot unbooted cards Daniel Palmer
@ 2026-07-31 12:19 ` Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 5/5] fbdev: tdfxfb: Program the initial video mode Daniel Palmer
2026-07-31 12:38 ` [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Helge Deller
5 siblings, 0 replies; 7+ messages in thread
From: Daniel Palmer @ 2026-07-31 12:19 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, Daniel Palmer
If the card was unbooted the VGA core needs to be woken up before
poking at it.
Signed-off-by: Daniel Palmer <daniel@0x0f.com>
---
drivers/video/fbdev/tdfxfb.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index 4d92ab5b5430..f2b658a47135 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -485,6 +485,10 @@ static void do_write_regs(struct fb_info *info, struct banshee_reg *reg)
tdfx_outl(par, MISCINIT1, tdfx_inl(par, MISCINIT1) | 0x01);
+ /* Wake the VGA core if it hasn't already been woken up */
+ tdfx_outl(par, VGAINIT0, reg->vgainit0);
+ vga_outb(par, 0x3c3, 0x01);
+
crt_outb(par, 0x11, crt_inb(par, 0x11) & 0x7f); /* CRT unprotect */
banshee_make_room(par, 3);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 5/5] fbdev: tdfxfb: Program the initial video mode
2026-07-31 12:19 [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Daniel Palmer
` (3 preceding siblings ...)
2026-07-31 12:19 ` [PATCH v2 4/5] fbdev: tdfxfb: Wake the VGA core before programming the CRTC Daniel Palmer
@ 2026-07-31 12:19 ` Daniel Palmer
2026-07-31 12:38 ` [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Helge Deller
5 siblings, 0 replies; 7+ messages in thread
From: Daniel Palmer @ 2026-07-31 12:19 UTC (permalink / raw)
To: deller; +Cc: linux-fbdev, dri-devel, linux-kernel, Daniel Palmer
If the card does not get bound to by fbcon set_par() never happens
and the initial video mode is not setup and the display detects
no signal.
Program the video mode and also clear the framebuffer memory so
random garbage isn't displayed.
Signed-off-by: Daniel Palmer <daniel@0x0f.com>
---
drivers/video/fbdev/tdfxfb.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c
index f2b658a47135..cb1c2e9f37db 100644
--- a/drivers/video/fbdev/tdfxfb.c
+++ b/drivers/video/fbdev/tdfxfb.c
@@ -1686,6 +1686,14 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_err_iobase;
}
+ /*
+ * Program a video mode and clear the framebuffer now, this
+ * ensures the display comes up even if fbcon doesn't bind
+ * when the framebuffer is registered.
+ */
+ tdfxfb_set_par(info);
+ memset_io(info->screen_base, 0, info->fix.smem_len);
+
if (register_framebuffer(info) < 0) {
printk(KERN_ERR "tdfxfb: can't register framebuffer\n");
fb_dealloc_cmap(&info->cmap);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work
2026-07-31 12:19 [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Daniel Palmer
` (4 preceding siblings ...)
2026-07-31 12:19 ` [PATCH v2 5/5] fbdev: tdfxfb: Program the initial video mode Daniel Palmer
@ 2026-07-31 12:38 ` Helge Deller
5 siblings, 0 replies; 7+ messages in thread
From: Helge Deller @ 2026-07-31 12:38 UTC (permalink / raw)
To: Daniel Palmer; +Cc: linux-fbdev, dri-devel, linux-kernel
On 7/31/26 14:19, Daniel Palmer wrote:
> Currently voodoo cards that weren't booted by the video BIOS aren't
> usable because the hardware is never brought up.
>
> This means if you happen to have a proper - non-x86 - computer,
> another card is the primary or you have a new BIOS that cannot
> run the video BIOS tdfxfb would probe but you will be staring at
> "no signal detected".
>
> This series implements manually booting cards using the
> values from the video BIOS' own table. Currently this only works
> for the voodoo 3.
>
> Debugging was partially assisted by Claude Fable 5. I could not
> work out why this would not work for months so I told it to
> create a voodoo 3 emulation for QEMU to compare the BIOS against
> my code. That made it obvious my init sequence matched the BIOS
> but the VGA core wasn't running.
>
> This has been tested on a real voodoo 3 in an x86-64 board and
> an Amiga 4000 with a mediator PCI bridge.
>
> I have some patches to add an interface for doing 3D rendering
> that are not included in this series and 3D rendering is also
> working so as far as I can tell everything is good.
>
> v2:
> - Fix up some poor 3am English mistakes in the cover letter.
> - Limit this to the voodoo 3. The BIOS layout is different
> for the 4/5 apparently.
> - Fixed the valid sashiko complaints - mainly clearing the
> framebuffer memory before fbcon might have bound, retaining
> the current behaviour if we can't get the video BIOS to get
> the config parameters.
> - Tweaked the wording of some of the commit messages.
>
> Daniel Palmer (5):
> fbdev: tdfxfb: Add helper to read config table from BIOS
> fbdev: tdfxfb: Attempt to detect if the card wasn't booted
> fbdev: tdfxfb: Manually boot unbooted cards
> fbdev: tdfxfb: Wake the VGA core before programming the CRTC
> fbdev: tdfxfb: Program the initial video mode
>
> drivers/video/fbdev/tdfxfb.c | 155 +++++++++++++++++++++++++++++++++++
> 1 file changed, 155 insertions(+)
New series applied to fbdev.
Thanks!
Helge
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-07-31 12:38 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 12:19 [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 1/5] fbdev: tdfxfb: Add helper to read config table from BIOS Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 2/5] fbdev: tdfxfb: Attempt to detect if the card wasn't booted Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 3/5] fbdev: tdfxfb: Manually boot unbooted cards Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 4/5] fbdev: tdfxfb: Wake the VGA core before programming the CRTC Daniel Palmer
2026-07-31 12:19 ` [PATCH v2 5/5] fbdev: tdfxfb: Program the initial video mode Daniel Palmer
2026-07-31 12:38 ` [PATCH v2 0/5] fbdev: tdfxfb: Make "unbooted" cards work Helge Deller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox