gfxmatrox: Introduce new programmer driver for Matrox Millenium cards
Supports a 64KiB parallel flash. ROM BAR configuration is tricky: The
register is only accessible when a special `biosen` bit is set. An OS
might not know this and is then unable to assign an address. We'll of-
fer to re-purpose the MMIO BAR address, but one has to be sure that no
graphics driver is running.
Datasheet used:
https://www.vintage3d.org/doc/matrox/2164spec.pdf
Change-Id: Ief6c05c8fbf752814d436ac3b47e87de0c977047
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/532
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/gfxmatrox.c b/gfxmatrox.c
new file mode 100644
index 0000000..647a908
--- /dev/null
+++ b/gfxmatrox.c
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the flashprog project.
+ *
+ * Copyright (C) 2026 Nico Huber <nico.h@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "programmer.h"
+#include "platform/pci.h"
+#include "hwaccess_physmap.h"
+#include "programmer/physmap.h"
+
+#define PCI_VENDOR_ID_MATROX 0x102b
+
+static const struct dev_entry gfx_matrox[] = {
+ {PCI_VENDOR_ID_MATROX, 0x051b, OK, "Matrox Electronics Systems Ltd.", "MGA 2164W [Millennium II]" },
+
+ {0},
+};
+
+static const struct par_master par_master_gfxmatrox = {
+ .chip_readb = mmio_chip_readb,
+ .chip_readw = mmio_chip_readw,
+ .chip_readl = mmio_chip_readl,
+ .chip_readn = mmio_chip_readn,
+ .chip_writeb = mmio_chip_writeb,
+ .chip_writew = mmio_chip_writew,
+ .chip_writel = mmio_chip_writel,
+ .chip_writen = fallback_chip_writen,
+ .map_flash = physmap,
+ .unmap_flash = physunmap,
+};
+
+static int gfxmatrox_init(struct flashprog_programmer *const prog)
+{
+ bool borrow_mmio_bar = false;
+ char *const bar_param = extract_programmer_param("bar");
+ if (bar_param) {
+ if (strcmp(bar_param, "no_gfx_driver_running") != 0) {
+ msg_perr("Error: Unknown argument for `bar' parameter: \"%s\".\n", bar_param);
+ free(bar_param);
+ return 1;
+ }
+ borrow_mmio_bar = true;
+ }
+ free(bar_param);
+
+ struct pci_dev *const matrox = pcidev_init(gfx_matrox, PCI_BASE_ADDRESS_1);
+ if (!matrox)
+ return 1;
+
+ const uint16_t command = pci_read_word(matrox, PCI_COMMAND);
+ if (!(command & PCI_COMMAND_MEMORY)) {
+ msg_perr("Error: PCI memory access is disabled.\n");
+ return 1;
+ }
+
+ /* Enable flash + writes. */
+ const uint32_t option = pci_read_long(matrox, 0x40);
+ rpci_write_long(matrox, 0x40, option | (1 << 30) | (1 << 20));
+
+ uint32_t rom_base = pci_read_long(matrox, PCI_ROM_ADDRESS);
+ if (!rom_base) {
+ if (!borrow_mmio_bar) {
+ msg_perr("Error: PCI ROM base is not configured.\n");
+ msg_perr("We can try to work around this if no graphics driver is running. When no driver\n");
+ msg_perr("is running, confirm this by supplying `-p gfxmatrox:bar=no_gfx_driver_running'.\n");
+ return 1;
+ }
+ /* If the ROM BAR isn't enabled, borrow the MMIO BAR. */
+ rom_base = pcidev_readbar(matrox, PCI_BASE_ADDRESS_1);
+ }
+ rpci_write_long(matrox, PCI_ROM_ADDRESS, rom_base | 1);
+
+ return register_par_master(&par_master_gfxmatrox, BUS_PARALLEL, rom_base & ~1u, 64*KiB, NULL);
+}
+
+const struct programmer_entry programmer_gfxmatrox = {
+ .name = "gfxmatrox",
+ .type = PCI,
+ .devs.dev = gfx_matrox,
+ .init = gfxmatrox_init,
+};