|
|
|
@ -1,32 +1,11 @@
|
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
/*
|
|
|
|
|
* vgaarb.c: Implements the VGA arbitration. For details refer to
|
|
|
|
|
* Documentation/gpu/vgaarbiter.rst
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* (C) Copyright 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
|
|
|
|
|
* (C) Copyright 2007 Paulo R. Zanoni <przanoni@gmail.com>
|
|
|
|
|
* (C) Copyright 2007, 2009 Tiago Vignatti <vignatti@freedesktop.org>
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice (including the next
|
|
|
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
|
|
|
* Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
* DEALINGS
|
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) "vgaarb: " fmt
|
|
|
|
@ -72,6 +51,7 @@ struct vga_device {
|
|
|
|
|
unsigned int io_norm_cnt; /* normal IO count */
|
|
|
|
|
unsigned int mem_norm_cnt; /* normal MEM count */
|
|
|
|
|
bool bridge_has_one_vga;
|
|
|
|
|
bool is_firmware_default; /* device selected by firmware */
|
|
|
|
|
unsigned int (*set_decode)(struct pci_dev *pdev, bool decode);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -122,8 +102,6 @@ both:
|
|
|
|
|
/* this is only used a cookie - it should not be dereferenced */
|
|
|
|
|
static struct pci_dev *vga_default;
|
|
|
|
|
|
|
|
|
|
static void vga_arb_device_card_gone(struct pci_dev *pdev);
|
|
|
|
|
|
|
|
|
|
/* Find somebody in our list */
|
|
|
|
|
static struct vga_device *vgadev_find(struct pci_dev *pdev)
|
|
|
|
|
{
|
|
|
|
@ -565,6 +543,144 @@ bail:
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(vga_put);
|
|
|
|
|
|
|
|
|
|
static bool vga_is_firmware_default(struct pci_dev *pdev)
|
|
|
|
|
{
|
|
|
|
|
#if defined(CONFIG_X86) || defined(CONFIG_IA64)
|
|
|
|
|
u64 base = screen_info.lfb_base;
|
|
|
|
|
u64 size = screen_info.lfb_size;
|
|
|
|
|
u64 limit;
|
|
|
|
|
resource_size_t start, end;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Select the device owning the boot framebuffer if there is one */
|
|
|
|
|
|
|
|
|
|
if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
|
|
|
|
|
base |= (u64)screen_info.ext_lfb_base << 32;
|
|
|
|
|
|
|
|
|
|
limit = base + size;
|
|
|
|
|
|
|
|
|
|
/* Does firmware framebuffer belong to us? */
|
|
|
|
|
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
|
|
|
|
|
flags = pci_resource_flags(pdev, i);
|
|
|
|
|
|
|
|
|
|
if ((flags & IORESOURCE_MEM) == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
start = pci_resource_start(pdev, i);
|
|
|
|
|
end = pci_resource_end(pdev, i);
|
|
|
|
|
|
|
|
|
|
if (!start || !end)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (base < start || limit >= end)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool vga_arb_integrated_gpu(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
#if defined(CONFIG_ACPI)
|
|
|
|
|
struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
|
|
|
|
|
|
|
|
return adev && !strcmp(acpi_device_hid(adev), ACPI_VIDEO_HID);
|
|
|
|
|
#else
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Return true if vgadev is a better default VGA device than the best one
|
|
|
|
|
* we've seen so far.
|
|
|
|
|
*/
|
|
|
|
|
static bool vga_is_boot_device(struct vga_device *vgadev)
|
|
|
|
|
{
|
|
|
|
|
struct vga_device *boot_vga = vgadev_find(vga_default_device());
|
|
|
|
|
struct pci_dev *pdev = vgadev->pdev;
|
|
|
|
|
u16 cmd, boot_cmd;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We select the default VGA device in this order:
|
|
|
|
|
* Firmware framebuffer (see vga_arb_select_default_device())
|
|
|
|
|
* Legacy VGA device (owns VGA_RSRC_LEGACY_MASK)
|
|
|
|
|
* Non-legacy integrated device (see vga_arb_select_default_device())
|
|
|
|
|
* Non-legacy discrete device (see vga_arb_select_default_device())
|
|
|
|
|
* Other device (see vga_arb_select_default_device())
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We always prefer a firmware default device, so if we've already
|
|
|
|
|
* found one, there's no need to consider vgadev.
|
|
|
|
|
*/
|
|
|
|
|
if (boot_vga && boot_vga->is_firmware_default)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (vga_is_firmware_default(pdev)) {
|
|
|
|
|
vgadev->is_firmware_default = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* A legacy VGA device has MEM and IO enabled and any bridges
|
|
|
|
|
* leading to it have PCI_BRIDGE_CTL_VGA enabled so the legacy
|
|
|
|
|
* resources ([mem 0xa0000-0xbffff], [io 0x3b0-0x3bb], etc) are
|
|
|
|
|
* routed to it.
|
|
|
|
|
*
|
|
|
|
|
* We use the first one we find, so if we've already found one,
|
|
|
|
|
* vgadev is no better.
|
|
|
|
|
*/
|
|
|
|
|
if (boot_vga &&
|
|
|
|
|
(boot_vga->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If we haven't found a legacy VGA device, accept a non-legacy
|
|
|
|
|
* device. It may have either IO or MEM enabled, and bridges may
|
|
|
|
|
* not have PCI_BRIDGE_CTL_VGA enabled, so it may not be able to
|
|
|
|
|
* use legacy VGA resources. Prefer an integrated GPU over others.
|
|
|
|
|
*/
|
|
|
|
|
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
|
|
|
|
if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* An integrated GPU overrides a previous non-legacy
|
|
|
|
|
* device. We expect only a single integrated GPU, but if
|
|
|
|
|
* there are more, we use the *last* because that was the
|
|
|
|
|
* previous behavior.
|
|
|
|
|
*/
|
|
|
|
|
if (vga_arb_integrated_gpu(&pdev->dev))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We prefer the first non-legacy discrete device we find.
|
|
|
|
|
* If we already found one, vgadev is no better.
|
|
|
|
|
*/
|
|
|
|
|
if (boot_vga) {
|
|
|
|
|
pci_read_config_word(boot_vga->pdev, PCI_COMMAND,
|
|
|
|
|
&boot_cmd);
|
|
|
|
|
if (boot_cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* vgadev has neither IO nor MEM enabled. If we haven't found any
|
|
|
|
|
* other VGA devices, it is the best candidate so far.
|
|
|
|
|
*/
|
|
|
|
|
if (!boot_vga)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Rules for using a bridge to control a VGA descendant decoding: if a bridge
|
|
|
|
|
* has only one VGA descendant then it can be used to control the VGA routing
|
|
|
|
@ -582,8 +698,10 @@ static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev)
|
|
|
|
|
|
|
|
|
|
vgadev->bridge_has_one_vga = true;
|
|
|
|
|
|
|
|
|
|
if (list_empty(&vga_list))
|
|
|
|
|
if (list_empty(&vga_list)) {
|
|
|
|
|
vgaarb_info(&vgadev->pdev->dev, "bridge control possible\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* okay iterate the new devices bridge hierarachy */
|
|
|
|
|
new_bus = vgadev->pdev->bus;
|
|
|
|
@ -622,6 +740,11 @@ static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev)
|
|
|
|
|
}
|
|
|
|
|
new_bus = new_bus->parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (vgadev->bridge_has_one_vga)
|
|
|
|
|
vgaarb_info(&vgadev->pdev->dev, "bridge control possible\n");
|
|
|
|
|
else
|
|
|
|
|
vgaarb_info(&vgadev->pdev->dev, "no bridge control possible\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -692,12 +815,10 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
|
|
|
|
|
bus = bus->parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Deal with VGA default device. Use first enabled one
|
|
|
|
|
* by default if arch doesn't have it's own hook
|
|
|
|
|
*/
|
|
|
|
|
if (vga_default == NULL &&
|
|
|
|
|
((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) {
|
|
|
|
|
vgaarb_info(&pdev->dev, "setting as boot VGA device\n");
|
|
|
|
|
if (vga_is_boot_device(vgadev)) {
|
|
|
|
|
vgaarb_info(&pdev->dev, "setting as boot VGA device%s\n",
|
|
|
|
|
vga_default_device() ?
|
|
|
|
|
" (overriding previous)" : "");
|
|
|
|
|
vga_set_default_device(pdev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -741,10 +862,6 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev)
|
|
|
|
|
/* Remove entry from list */
|
|
|
|
|
list_del(&vgadev->list);
|
|
|
|
|
vga_count--;
|
|
|
|
|
/* Notify userland driver that the device is gone so it discards
|
|
|
|
|
* it's copies of the pci_dev pointer
|
|
|
|
|
*/
|
|
|
|
|
vga_arb_device_card_gone(pdev);
|
|
|
|
|
|
|
|
|
|
/* Wake up all possible waiters */
|
|
|
|
|
wake_up_all(&vga_wait_queue);
|
|
|
|
@ -994,9 +1111,7 @@ static ssize_t vga_arb_read(struct file *file, char __user *buf,
|
|
|
|
|
if (lbuf == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
/* Shields against vga_arb_device_card_gone (pci_dev going
|
|
|
|
|
* away), and allows access to vga list
|
|
|
|
|
*/
|
|
|
|
|
/* Protects vga_list */
|
|
|
|
|
spin_lock_irqsave(&vga_lock, flags);
|
|
|
|
|
|
|
|
|
|
/* If we are targeting the default, use it */
|
|
|
|
@ -1013,8 +1128,6 @@ static ssize_t vga_arb_read(struct file *file, char __user *buf,
|
|
|
|
|
/* Wow, it's not in the list, that shouldn't happen,
|
|
|
|
|
* let's fix us up and return invalid card
|
|
|
|
|
*/
|
|
|
|
|
if (pdev == priv->target)
|
|
|
|
|
vga_arb_device_card_gone(pdev);
|
|
|
|
|
spin_unlock_irqrestore(&vga_lock, flags);
|
|
|
|
|
len = sprintf(lbuf, "invalid");
|
|
|
|
|
goto done;
|
|
|
|
@ -1022,7 +1135,7 @@ static ssize_t vga_arb_read(struct file *file, char __user *buf,
|
|
|
|
|
|
|
|
|
|
/* Fill the buffer with infos */
|
|
|
|
|
len = snprintf(lbuf, 1024,
|
|
|
|
|
"count:%d,PCI:%s,decodes=%s,owns=%s,locks=%s(%d:%d)\n",
|
|
|
|
|
"count:%d,PCI:%s,decodes=%s,owns=%s,locks=%s(%u:%u)\n",
|
|
|
|
|
vga_decode_count, pci_name(pdev),
|
|
|
|
|
vga_iostate_to_str(vgadev->decodes),
|
|
|
|
|
vga_iostate_to_str(vgadev->owns),
|
|
|
|
@ -1358,10 +1471,6 @@ static int vga_arb_release(struct inode *inode, struct file *file)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void vga_arb_device_card_gone(struct pci_dev *pdev)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* callback any registered clients to let them know we have a
|
|
|
|
|
* change in VGA cards
|
|
|
|
@ -1430,111 +1539,10 @@ static struct miscdevice vga_arb_device = {
|
|
|
|
|
MISC_DYNAMIC_MINOR, "vga_arbiter", &vga_arb_device_fops
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_ACPI)
|
|
|
|
|
static bool vga_arb_integrated_gpu(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
|
|
|
|
|
|
|
|
return adev && !strcmp(acpi_device_hid(adev), ACPI_VIDEO_HID);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
static bool vga_arb_integrated_gpu(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void __init vga_arb_select_default_device(void)
|
|
|
|
|
{
|
|
|
|
|
struct pci_dev *pdev, *found = NULL;
|
|
|
|
|
struct vga_device *vgadev;
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_X86) || defined(CONFIG_IA64)
|
|
|
|
|
u64 base = screen_info.lfb_base;
|
|
|
|
|
u64 size = screen_info.lfb_size;
|
|
|
|
|
u64 limit;
|
|
|
|
|
resource_size_t start, end;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
|
|
|
|
|
base |= (u64)screen_info.ext_lfb_base << 32;
|
|
|
|
|
|
|
|
|
|
limit = base + size;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(vgadev, &vga_list, list) {
|
|
|
|
|
struct device *dev = &vgadev->pdev->dev;
|
|
|
|
|
/*
|
|
|
|
|
* Override vga_arbiter_add_pci_device()'s I/O based detection
|
|
|
|
|
* as it may take the wrong device (e.g. on Apple system under
|
|
|
|
|
* EFI).
|
|
|
|
|
*
|
|
|
|
|
* Select the device owning the boot framebuffer if there is
|
|
|
|
|
* one.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Does firmware framebuffer belong to us? */
|
|
|
|
|
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
|
|
|
|
|
flags = pci_resource_flags(vgadev->pdev, i);
|
|
|
|
|
|
|
|
|
|
if ((flags & IORESOURCE_MEM) == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
start = pci_resource_start(vgadev->pdev, i);
|
|
|
|
|
end = pci_resource_end(vgadev->pdev, i);
|
|
|
|
|
|
|
|
|
|
if (!start || !end)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (base < start || limit >= end)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!vga_default_device())
|
|
|
|
|
vgaarb_info(dev, "setting as boot device\n");
|
|
|
|
|
else if (vgadev->pdev != vga_default_device())
|
|
|
|
|
vgaarb_info(dev, "overriding boot device\n");
|
|
|
|
|
vga_set_default_device(vgadev->pdev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (!vga_default_device()) {
|
|
|
|
|
list_for_each_entry_reverse(vgadev, &vga_list, list) {
|
|
|
|
|
struct device *dev = &vgadev->pdev->dev;
|
|
|
|
|
u16 cmd;
|
|
|
|
|
|
|
|
|
|
pdev = vgadev->pdev;
|
|
|
|
|
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
|
|
|
|
if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
|
|
|
|
|
found = pdev;
|
|
|
|
|
if (vga_arb_integrated_gpu(dev))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
|
vgaarb_info(&found->dev, "setting as boot device (VGA legacy resources not available)\n");
|
|
|
|
|
vga_set_default_device(found);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!vga_default_device()) {
|
|
|
|
|
vgadev = list_first_entry_or_null(&vga_list,
|
|
|
|
|
struct vga_device, list);
|
|
|
|
|
if (vgadev) {
|
|
|
|
|
struct device *dev = &vgadev->pdev->dev;
|
|
|
|
|
vgaarb_info(dev, "setting as boot device (VGA legacy resources not available)\n");
|
|
|
|
|
vga_set_default_device(vgadev->pdev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int __init vga_arb_device_init(void)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
struct pci_dev *pdev;
|
|
|
|
|
struct vga_device *vgadev;
|
|
|
|
|
|
|
|
|
|
rc = misc_register(&vga_arb_device);
|
|
|
|
|
if (rc < 0)
|
|
|
|
@ -1550,18 +1558,7 @@ static int __init vga_arb_device_init(void)
|
|
|
|
|
PCI_ANY_ID, pdev)) != NULL)
|
|
|
|
|
vga_arbiter_add_pci_device(pdev);
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(vgadev, &vga_list, list) {
|
|
|
|
|
struct device *dev = &vgadev->pdev->dev;
|
|
|
|
|
|
|
|
|
|
if (vgadev->bridge_has_one_vga)
|
|
|
|
|
vgaarb_info(dev, "bridge control possible\n");
|
|
|
|
|
else
|
|
|
|
|
vgaarb_info(dev, "no bridge control possible\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vga_arb_select_default_device();
|
|
|
|
|
|
|
|
|
|
pr_info("loaded\n");
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
subsys_initcall(vga_arb_device_init);
|
|
|
|
|
subsys_initcall_sync(vga_arb_device_init);
|