[PATCH v2] PCI: vgaarb: Include 0x0380 devices in boot VGA selection

From: Aaron Ma

Date: Mon Jun 22 2026 - 02:14:16 EST


Some firmware boot displays use PCI class 0x0380
(PCI_CLASS_DISPLAY_OTHER). vgaarb only registers pci_is_vga()
devices, so those devices are skipped by vga_is_firmware_default()
and cannot become vga_default_device().

On hybrid systems this can leave a discrete VGA GPU as
vga_default_device() even when the firmware framebuffer is on the
0x0380 device. That makes boot_vga point at the wrong GPU and can
make boot_display report multiple devices through different paths.

Register legacy VGA and 0x0380 display devices for default selection
and boot_vga visibility. Keep legacy VGA decode ownership limited to
pci_is_vga() devices so 0x0380 devices are not treated as legacy VGA
decoders.

Signed-off-by: Aaron Ma <aaron.ma@xxxxxxxxxxxxx>
---
Changes in v2:
- Keep legacy VGA decodes and owns limited to pci_is_vga() devices.
- Keep 0x0380 devices eligible for firmware/default selection and
boot_vga visibility without treating them as legacy VGA decoders.
- Update the commit message to describe the boot_vga compatibility goal.

drivers/pci/pci-sysfs.c | 3 ++-
drivers/pci/vgaarb.c | 27 +++++++++++++++------------
include/linux/pci.h | 14 ++++++++++++++
3 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index d37860841260c..843d83ec9550a 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1717,7 +1717,8 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct pci_dev *pdev = to_pci_dev(dev);

- if (a == &dev_attr_boot_vga.attr && pci_is_vga(pdev))
+ if (a == &dev_attr_boot_vga.attr &&
+ pci_is_vga_or_other_display(pdev))
return a->mode;

if (a == &dev_attr_serial_number.attr && pci_get_dsn(pdev))
diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c
index c360eee11dd9e..56a86779e6d28 100644
--- a/drivers/pci/vgaarb.c
+++ b/drivers/pci/vgaarb.c
@@ -741,6 +741,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
unsigned long flags;
struct pci_bus *bus;
struct pci_dev *bridge;
+ bool legacy_vga = pci_is_vga(pdev);
u16 cmd;

/* Allocate structure */
@@ -762,21 +763,23 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
}
vgadev->pdev = pdev;

- /* By default, assume we decode everything */
- vgadev->decodes = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
- VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
+ /* By default, assume VGA devices decode everything */
+ vgadev->decodes = VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
+ if (legacy_vga)
+ vgadev->decodes |= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;

- /* By default, mark it as decoding */
- vga_decode_count++;
+ /* By default, mark legacy VGA devices as decoding */
+ if (vgadev->decodes & VGA_RSRC_LEGACY_MASK)
+ vga_decode_count++;

/*
* Mark that we "own" resources based on our enables, we will
* clear that below if the bridge isn't forwarding.
*/
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
- if (cmd & PCI_COMMAND_IO)
+ if (legacy_vga && (cmd & PCI_COMMAND_IO))
vgadev->owns |= VGA_RSRC_LEGACY_IO;
- if (cmd & PCI_COMMAND_MEMORY)
+ if (legacy_vga && (cmd & PCI_COMMAND_MEMORY))
vgadev->owns |= VGA_RSRC_LEGACY_MEM;

/* Check if VGA cycles can get down to us */
@@ -796,7 +799,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
}

if (vga_is_boot_device(vgadev)) {
- vgaarb_info(&pdev->dev, "setting as boot VGA device%s\n",
+ vgaarb_info(&pdev->dev, "setting as boot display device%s\n",
vga_default_device() ?
" (overriding previous)" : "");
vga_set_default_device(pdev);
@@ -1483,8 +1486,8 @@ static int pci_notify(struct notifier_block *nb, unsigned long action,

vgaarb_dbg(dev, "%s\n", __func__);

- /* Only deal with VGA class devices */
- if (!pci_is_vga(pdev))
+ /* Only deal with legacy VGA and other display controller devices */
+ if (!pci_is_vga_or_other_display(pdev))
return 0;

/*
@@ -1530,12 +1533,12 @@ static int __init vga_arb_device_init(void)

bus_register_notifier(&pci_bus_type, &pci_notifier);

- /* Add all VGA class PCI devices by default */
+ /* Add legacy VGA and other display controller PCI devices by default */
pdev = NULL;
while ((pdev =
pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_ANY_ID, pdev)) != NULL) {
- if (pci_is_vga(pdev))
+ if (pci_is_vga_or_other_display(pdev))
vga_arbiter_add_pci_device(pdev);
}

diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2c4454583c115..195ec1bdac863 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -792,6 +792,20 @@ static inline bool pci_is_vga(struct pci_dev *pdev)
return false;
}

+/**
+ * pci_is_vga_or_other_display - check if the PCI device is VGA or 0x0380
+ * @pdev: PCI device
+ *
+ * Return true for legacy VGA-compatible devices and for "other display
+ * controller" devices. Some firmware-selected boot display devices expose
+ * class 0x0380 instead of PCI_CLASS_DISPLAY_VGA.
+ */
+static inline bool pci_is_vga_or_other_display(struct pci_dev *pdev)
+{
+ return pci_is_vga(pdev) ||
+ (pdev->class >> 8) == PCI_CLASS_DISPLAY_OTHER;
+}
+
/**
* pci_is_display - check if the PCI device is a display controller
* @pdev: PCI device
--
2.43.0