mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-22 17:01:14 +00:00
Merge branch 'pci/vga'
- Move vgaarb.c from drivers/gpu/vga to drivers/pci (Bjorn Helgaas) - Factor out default VGA device selection (Huacai Chen) - Move firmware default device detection to ADD_DEVICE path so we can select a default device regardless of whether it is enumerated before or after vga_arb_device_init() (Huacai Chen) - Move non-legacy VGA detection to ADD_DEVICE path (Huacai Chen) - Move disabled VGA device detection to ADD_DEVICE path (Huacai Chen) * pci/vga: PCI/VGA: Replace full MIT license text with SPDX identifier PCI/VGA: Use unsigned format string to print lock counts PCI/VGA: Log bridge control messages when adding devices PCI/VGA: Remove empty vga_arb_device_card_gone() PCI/VGA: Move disabled VGA device detection to ADD_DEVICE path PCI/VGA: Move non-legacy VGA detection to ADD_DEVICE path PCI/VGA: Move firmware default device detection to ADD_DEVICE path PCI/VGA: Factor out default VGA device selection PCI/VGA: Factor out vga_select_framebuffer_device() PCI/VGA: Move vga_arb_integrated_gpu() earlier in file PCI/VGA: Move vgaarb to drivers/pci
This commit is contained in:
commit
816f891704
6 changed files with 176 additions and 179 deletions
|
@ -100,7 +100,7 @@ In-kernel interface
|
|||
.. kernel-doc:: include/linux/vgaarb.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/vga/vgaarb.c
|
||||
.. kernel-doc:: drivers/pci/vgaarb.c
|
||||
:export:
|
||||
|
||||
libpciaccess
|
||||
|
|
|
@ -1,23 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config VGA_ARB
|
||||
bool "VGA Arbitration" if EXPERT
|
||||
default y
|
||||
depends on (PCI && !S390)
|
||||
help
|
||||
Some "legacy" VGA devices implemented on PCI typically have the same
|
||||
hard-decoded addresses as they did on ISA. When multiple PCI devices
|
||||
are accessed at same time they need some kind of coordination. Please
|
||||
see Documentation/gpu/vgaarbiter.rst for more details. Select this to
|
||||
enable VGA arbiter.
|
||||
|
||||
config VGA_ARB_MAX_GPUS
|
||||
int "Maximum number of GPUs"
|
||||
default 16
|
||||
depends on VGA_ARB
|
||||
help
|
||||
Reserves space in the kernel to maintain resource locking for
|
||||
multiple GPUS. The overhead for each GPU is very small.
|
||||
|
||||
config VGA_SWITCHEROO
|
||||
bool "Laptop Hybrid Graphics - GPU switching support"
|
||||
depends on X86
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_VGA_ARB) += vgaarb.o
|
||||
obj-$(CONFIG_VGA_SWITCHEROO) += vga_switcheroo.o
|
||||
|
|
|
@ -252,6 +252,25 @@ config PCIE_BUS_PEER2PEER
|
|||
|
||||
endchoice
|
||||
|
||||
config VGA_ARB
|
||||
bool "VGA Arbitration" if EXPERT
|
||||
default y
|
||||
depends on (PCI && !S390)
|
||||
help
|
||||
Some "legacy" VGA devices implemented on PCI typically have the same
|
||||
hard-decoded addresses as they did on ISA. When multiple PCI devices
|
||||
are accessed at same time they need some kind of coordination. Please
|
||||
see Documentation/gpu/vgaarbiter.rst for more details. Select this to
|
||||
enable VGA arbiter.
|
||||
|
||||
config VGA_ARB_MAX_GPUS
|
||||
int "Maximum number of GPUs"
|
||||
default 16
|
||||
depends on VGA_ARB
|
||||
help
|
||||
Reserves space in the kernel to maintain resource locking for
|
||||
multiple GPUS. The overhead for each GPU is very small.
|
||||
|
||||
source "drivers/pci/hotplug/Kconfig"
|
||||
source "drivers/pci/controller/Kconfig"
|
||||
source "drivers/pci/endpoint/Kconfig"
|
||||
|
|
|
@ -30,6 +30,7 @@ obj-$(CONFIG_PCI_PF_STUB) += pci-pf-stub.o
|
|||
obj-$(CONFIG_PCI_ECAM) += ecam.o
|
||||
obj-$(CONFIG_PCI_P2PDMA) += p2pdma.o
|
||||
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
|
||||
obj-$(CONFIG_VGA_ARB) += vgaarb.o
|
||||
|
||||
# Endpoint library must be initialized before its users
|
||||
obj-$(CONFIG_PCI_ENDPOINT) += endpoint/
|
||||
|
|
|
@ -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 @@ static int vga_str_to_iostate(char *buf, int str_size, int *io_state)
|
|||
/* 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 @@ void vga_put(struct pci_dev *pdev, unsigned int rsrc)
|
|||
}
|
||||
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);
|
Loading…
Reference in a new issue