From: Sui Jingfeng <suijingfeng@loongson.cn>
The vga_is_firmware_default() function is arch-dependent, which dosen't
sound right. At least, It also works on Mips and LoongArch platform with
the drm/AMDGPU and drm/radeon driver. However, it's difficult to enumerate
all Arch-driver combination. I'm wrong if there are only one exception.
With the observation that device drivers typically has better knowledge
about which PCI bar contains the firmware framebuffer, Which could helps
to avoids the need to iterate all of the PCI BARs. But As a PCI core
function at pci/vgaarb.c, vga_is_firmware_default funciton is embarrasing
to make optimization for a specific device.
There also has PCI display controller don't has a dedicated VRAM bar in
the world, this function will lost the effectiveness on such a case.
Luckily, device driver can do the accurate workaround. We add a callback
then, to let the device driver make the decision. Device drivers know what
archs it could works, device driver also can shipping this decision to
whatever arch its device can reach, not only the X86 and IA64.
Also honor the comment: "Clients have two callback mechanisms they can use"
Signed-off-by: Sui Jingfeng <suijingfeng@loongson.cn>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 +-
drivers/gpu/drm/i915/display/intel_vga.c | 3 +--
drivers/gpu/drm/nouveau/nouveau_vga.c | 2 +-
drivers/gpu/drm/radeon/radeon_device.c | 2 +-
drivers/pci/vgaarb.c | 22 ++++++++++++++++++----
drivers/vfio/pci/vfio_pci_core.c | 2 +-
include/linux/vgaarb.h | 8 +++++---
7 files changed, 28 insertions(+), 13 deletions(-)
@@ -3960,7 +3960,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
/* this will fail for cards that aren't VGA class devices, just
* ignore it */
if ((adev->pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
- vga_client_register(adev->pdev, amdgpu_device_vga_set_decode);
+ vga_client_register(adev->pdev, amdgpu_device_vga_set_decode, NULL);
px = amdgpu_device_supports_px(ddev);
@@ -115,7 +115,6 @@ intel_vga_set_decode(struct pci_dev *pdev, bool enable_decode)
int intel_vga_register(struct drm_i915_private *i915)
{
-
struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
int ret;
@@ -127,7 +126,7 @@ int intel_vga_register(struct drm_i915_private *i915)
* then we do not take part in VGA arbitration and the
* vga_client_register() fails with -ENODEV.
*/
- ret = vga_client_register(pdev, intel_vga_set_decode);
+ ret = vga_client_register(pdev, intel_vga_set_decode, NULL);
if (ret && ret != -ENODEV)
return ret;
@@ -92,7 +92,7 @@ nouveau_vga_init(struct nouveau_drm *drm)
return;
pdev = to_pci_dev(dev->dev);
- vga_client_register(pdev, nouveau_vga_set_decode);
+ vga_client_register(pdev, nouveau_vga_set_decode, NULL);
/* don't register Thunderbolt eGPU with vga_switcheroo */
if (pci_is_thunderbolt_attached(pdev))
@@ -1425,7 +1425,7 @@ int radeon_device_init(struct radeon_device *rdev,
/* if we have > 1 VGA cards, then disable the radeon VGA resources */
/* this will fail for cards that aren't VGA class devices, just
* ignore it */
- vga_client_register(rdev->pdev, radeon_vga_set_decode);
+ vga_client_register(rdev->pdev, radeon_vga_set_decode, NULL);
if (rdev->flags & RADEON_IS_PX)
runtime = true;
@@ -53,6 +53,7 @@ struct vga_device {
bool bridge_has_one_vga;
bool is_firmware_default; /* device selected by firmware */
unsigned int (*set_decode)(struct pci_dev *pdev, bool decode);
+ bool (*is_boot_device)(struct pci_dev *pdev);
};
static LIST_HEAD(vga_list);
@@ -615,10 +616,17 @@ static bool vga_is_boot_device(struct vga_device *vgadev)
if (boot_vga && boot_vga->is_firmware_default)
return false;
- if (vga_is_firmware_default(pdev)) {
- vgadev->is_firmware_default = true;
+ /*
+ * Ask the device driver first, if registered. Fallback to the
+ * default implement if the callback is non-exist.
+ */
+ if (vgadev->is_boot_device)
+ vgadev->is_firmware_default = vgadev->is_boot_device(pdev);
+ else
+ vgadev->is_firmware_default = vga_is_firmware_default(pdev);
+
+ if (vgadev->is_firmware_default)
return true;
- }
/*
* A legacy VGA device has MEM and IO enabled and any bridges
@@ -958,6 +966,10 @@ EXPORT_SYMBOL(vga_set_legacy_decoding);
* @set_decode callback: If a client can disable its GPU VGA resource, it
* will get a callback from this to set the encode/decode state.
*
+ * @is_boot_device: callback to the device driver, query if a client is the
+ * default boot device, as the device driver typically has better knowledge
+ * if specific device is the boot device. But this callback is optional.
+ *
* Rationale: we cannot disable VGA decode resources unconditionally, some
* single GPU laptops seem to require ACPI or BIOS access to the VGA registers
* to control things like backlights etc. Hopefully newer multi-GPU laptops do
@@ -973,7 +985,8 @@ EXPORT_SYMBOL(vga_set_legacy_decoding);
* Returns: 0 on success, -1 on failure
*/
int vga_client_register(struct pci_dev *pdev,
- unsigned int (*set_decode)(struct pci_dev *pdev, bool decode))
+ unsigned int (*set_decode)(struct pci_dev *pdev, bool decode),
+ bool (*is_boot_device)(struct pci_dev *pdev))
{
int ret = -ENODEV;
struct vga_device *vgadev;
@@ -985,6 +998,7 @@ int vga_client_register(struct pci_dev *pdev,
goto bail;
vgadev->set_decode = set_decode;
+ vgadev->is_boot_device = is_boot_device;
ret = 0;
bail:
@@ -2067,7 +2067,7 @@ static int vfio_pci_vga_init(struct vfio_pci_core_device *vdev)
if (ret)
return ret;
- ret = vga_client_register(pdev, vfio_pci_set_decode);
+ ret = vga_client_register(pdev, vfio_pci_set_decode, NULL);
if (ret)
return ret;
vga_set_legacy_decoding(pdev, vfio_pci_set_decode(pdev, false));
@@ -50,7 +50,8 @@ struct pci_dev *vga_default_device(void);
void vga_set_default_device(struct pci_dev *pdev);
int vga_remove_vgacon(struct pci_dev *pdev);
int vga_client_register(struct pci_dev *pdev,
- unsigned int (*set_decode)(struct pci_dev *pdev, bool state));
+ unsigned int (*set_decode)(struct pci_dev *pdev, bool state),
+ bool (*is_boot_device)(struct pci_dev *pdev));
#else /* CONFIG_VGA_ARB */
static inline void vga_set_legacy_decoding(struct pci_dev *pdev,
unsigned int decodes)
@@ -76,7 +77,8 @@ static inline int vga_remove_vgacon(struct pci_dev *pdev)
return 0;
}
static inline int vga_client_register(struct pci_dev *pdev,
- unsigned int (*set_decode)(struct pci_dev *pdev, bool state))
+ unsigned int (*set_decode)(struct pci_dev *pdev, bool state),
+ bool (*is_boot_device)(struct pci_dev *pdev))
{
return 0;
}
@@ -114,7 +116,7 @@ static inline int vga_get_uninterruptible(struct pci_dev *pdev,
static inline void vga_client_unregister(struct pci_dev *pdev)
{
- vga_client_register(pdev, NULL);
+ vga_client_register(pdev, NULL, NULL);
}
#endif /* LINUX_VGA_H */