aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/vga/vgaarb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/vga/vgaarb.c')
-rw-r--r--drivers/gpu/vga/vgaarb.c221
1 files changed, 152 insertions, 69 deletions
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index c380c65da41..af025970835 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -61,7 +61,7 @@ struct vga_device {
unsigned int mem_lock_cnt; /* legacy MEM lock count */
unsigned int io_norm_cnt; /* normal IO count */
unsigned int mem_norm_cnt; /* normal MEM count */
-
+ bool bridge_has_one_vga;
/* allow IRQ enable/disable hook */
void *cookie;
void (*irq_set_state)(void *cookie, bool enable);
@@ -136,6 +136,17 @@ struct pci_dev *vga_default_device(void)
{
return vga_default;
}
+
+EXPORT_SYMBOL_GPL(vga_default_device);
+
+void vga_set_default_device(struct pci_dev *pdev)
+{
+ if (vga_default == pdev)
+ return;
+
+ pci_dev_put(vga_default);
+ vga_default = pci_dev_get(pdev);
+}
#endif
static inline void vga_irq_set_state(struct vga_device *vgadev, bool state)
@@ -151,7 +162,7 @@ static inline void vga_irq_set_state(struct vga_device *vgadev, bool state)
static void vga_check_first_use(void)
{
/* we should inform all GPUs in the system that
- * VGA arb has occured and to try and disable resources
+ * VGA arb has occurred and to try and disable resources
* if they can */
if (!vga_arbiter_used) {
vga_arbiter_used = true;
@@ -165,6 +176,8 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev,
unsigned int wants, legacy_wants, match;
struct vga_device *conflict;
unsigned int pci_bits;
+ u32 flags = 0;
+
/* Account for "normal" resources to lock. If we decode the legacy,
* counterpart, we need to request it as well
*/
@@ -237,21 +250,28 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev,
/* looks like he doesn't have a lock, we can steal
* them from him
*/
- vga_irq_set_state(conflict, false);
+ flags = 0;
pci_bits = 0;
- if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
- pci_bits |= PCI_COMMAND_MEMORY;
- if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
- pci_bits |= PCI_COMMAND_IO;
- pci_set_vga_state(conflict->pdev, false, pci_bits,
- change_bridge);
- conflict->owns &= ~lwants;
+ if (!conflict->bridge_has_one_vga) {
+ vga_irq_set_state(conflict, false);
+ flags |= PCI_VGA_STATE_CHANGE_DECODES;
+ if (match & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
+ pci_bits |= PCI_COMMAND_MEMORY;
+ if (match & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
+ pci_bits |= PCI_COMMAND_IO;
+ }
+
+ if (change_bridge)
+ flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
+
+ pci_set_vga_state(conflict->pdev, false, pci_bits, flags);
+ conflict->owns &= ~match;
/* If he also owned non-legacy, that is no longer the case */
- if (lwants & VGA_RSRC_LEGACY_MEM)
+ if (match & VGA_RSRC_LEGACY_MEM)
conflict->owns &= ~VGA_RSRC_NORMAL_MEM;
- if (lwants & VGA_RSRC_LEGACY_IO)
+ if (match & VGA_RSRC_LEGACY_IO)
conflict->owns &= ~VGA_RSRC_NORMAL_IO;
}
@@ -261,14 +281,24 @@ enable_them:
* also have in "decodes". We can lock resources we don't decode but
* not own them.
*/
+ flags = 0;
pci_bits = 0;
- if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
- pci_bits |= PCI_COMMAND_MEMORY;
- if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
- pci_bits |= PCI_COMMAND_IO;
- pci_set_vga_state(vgadev->pdev, true, pci_bits, !!(wants & VGA_RSRC_LEGACY_MASK));
- vga_irq_set_state(vgadev, true);
+ if (!vgadev->bridge_has_one_vga) {
+ flags |= PCI_VGA_STATE_CHANGE_DECODES;
+ if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
+ pci_bits |= PCI_COMMAND_MEMORY;
+ if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
+ pci_bits |= PCI_COMMAND_IO;
+ }
+ if (!!(wants & VGA_RSRC_LEGACY_MASK))
+ flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
+
+ pci_set_vga_state(vgadev->pdev, true, pci_bits, flags);
+
+ if (!vgadev->bridge_has_one_vga) {
+ vga_irq_set_state(vgadev, true);
+ }
vgadev->owns |= (wants & vgadev->decodes);
lock_them:
vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK);
@@ -421,6 +451,60 @@ bail:
}
EXPORT_SYMBOL(vga_put);
+/* 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 for that device.
+ It should always use the bridge closest to the device to control it.
+ If a bridge has a direct VGA descendant, but also have a sub-bridge
+ VGA descendant then we cannot use that bridge to control the direct VGA descendant.
+ So for every device we register, we need to iterate all its parent bridges
+ so we can invalidate any devices using them properly.
+*/
+static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev)
+{
+ struct vga_device *same_bridge_vgadev;
+ struct pci_bus *new_bus, *bus;
+ struct pci_dev *new_bridge, *bridge;
+
+ vgadev->bridge_has_one_vga = true;
+
+ if (list_empty(&vga_list))
+ return;
+
+ /* okay iterate the new devices bridge hierarachy */
+ new_bus = vgadev->pdev->bus;
+ while (new_bus) {
+ new_bridge = new_bus->self;
+
+ /* go through list of devices already registered */
+ list_for_each_entry(same_bridge_vgadev, &vga_list, list) {
+ bus = same_bridge_vgadev->pdev->bus;
+ bridge = bus->self;
+
+ /* see if the share a bridge with this device */
+ if (new_bridge == bridge) {
+ /* if their direct parent bridge is the same
+ as any bridge of this device then it can't be used
+ for that device */
+ same_bridge_vgadev->bridge_has_one_vga = false;
+ }
+
+ /* now iterate the previous devices bridge hierarchy */
+ /* if the new devices parent bridge is in the other devices
+ hierarchy then we can't use it to control this device */
+ while (bus) {
+ bridge = bus->self;
+ if (bridge) {
+ if (bridge == vgadev->pdev->bus->self)
+ vgadev->bridge_has_one_vga = false;
+ }
+ bus = bus->parent;
+ }
+ }
+ new_bus = new_bus->parent;
+ }
+}
+
/*
* Currently, we assume that the "initial" setup of the system is
* not sane, that is we come up with conflicting devices and let
@@ -497,9 +581,11 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
if (vga_default == NULL &&
((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK))
- vga_default = pci_dev_get(pdev);
+ vga_set_default_device(pdev);
#endif
+ vga_arbiter_check_bridge_sharing(vgadev);
+
/* Add to the list */
list_add(&vgadev->list, &vga_list);
vga_count++;
@@ -530,10 +616,10 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev)
goto bail;
}
- if (vga_default == pdev) {
- pci_dev_put(vga_default);
- vga_default = NULL;
- }
+#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
+ if (vga_default == pdev)
+ vga_set_default_device(NULL);
+#endif
if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM))
vga_decode_count--;
@@ -558,10 +644,12 @@ bail:
static inline void vga_update_device_decodes(struct vga_device *vgadev,
int new_decodes)
{
- int old_decodes;
- struct vga_device *new_vgadev, *conflict;
+ int old_decodes, decodes_removed, decodes_unlocked;
old_decodes = vgadev->decodes;
+ decodes_removed = ~new_decodes & old_decodes;
+ decodes_unlocked = vgadev->locks & decodes_removed;
+ vgadev->owns &= ~decodes_removed;
vgadev->decodes = new_decodes;
pr_info("vgaarb: device changed decodes: PCI:%s,olddecodes=%s,decodes=%s:owns=%s\n",
@@ -570,31 +658,22 @@ static inline void vga_update_device_decodes(struct vga_device *vgadev,
vga_iostate_to_str(vgadev->decodes),
vga_iostate_to_str(vgadev->owns));
-
- /* if we own the decodes we should move them along to
- another card */
- if ((vgadev->owns & old_decodes) && (vga_count > 1)) {
- /* set us to own nothing */
- vgadev->owns &= ~old_decodes;
- list_for_each_entry(new_vgadev, &vga_list, list) {
- if ((new_vgadev != vgadev) &&
- (new_vgadev->decodes & VGA_RSRC_LEGACY_MASK)) {
- pr_info("vgaarb: transferring owner from PCI:%s to PCI:%s\n", pci_name(vgadev->pdev), pci_name(new_vgadev->pdev));
- conflict = __vga_tryget(new_vgadev, VGA_RSRC_LEGACY_MASK);
- if (!conflict)
- __vga_put(new_vgadev, VGA_RSRC_LEGACY_MASK);
- break;
- }
- }
+ /* if we removed locked decodes, lock count goes to zero, and release */
+ if (decodes_unlocked) {
+ if (decodes_unlocked & VGA_RSRC_LEGACY_IO)
+ vgadev->io_lock_cnt = 0;
+ if (decodes_unlocked & VGA_RSRC_LEGACY_MEM)
+ vgadev->mem_lock_cnt = 0;
+ __vga_put(vgadev, decodes_unlocked);
}
/* change decodes counter */
- if (old_decodes != new_decodes) {
- if (new_decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM))
- vga_decode_count++;
- else
- vga_decode_count--;
- }
+ if (old_decodes & VGA_RSRC_LEGACY_MASK &&
+ !(new_decodes & VGA_RSRC_LEGACY_MASK))
+ vga_decode_count--;
+ if (!(old_decodes & VGA_RSRC_LEGACY_MASK) &&
+ new_decodes & VGA_RSRC_LEGACY_MASK)
+ vga_decode_count++;
pr_debug("vgaarb: decoding count now is: %d\n", vga_decode_count);
}
@@ -636,7 +715,7 @@ int vga_client_register(struct pci_dev *pdev, void *cookie,
void (*irq_set_state)(void *cookie, bool state),
unsigned int (*set_vga_decode)(void *cookie, bool decode))
{
- int ret = -1;
+ int ret = -ENODEV;
struct vga_device *vgadev;
unsigned long flags;
@@ -774,7 +853,7 @@ static ssize_t vga_arb_read(struct file *file, char __user * buf,
*/
spin_lock_irqsave(&vga_lock, flags);
- /* If we are targetting the default, use it */
+ /* If we are targeting the default, use it */
pdev = priv->target;
if (pdev == NULL || pdev == PCI_INVALID_CARD) {
spin_unlock_irqrestore(&vga_lock, flags);
@@ -916,14 +995,20 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
uc = &priv->cards[i];
}
- if (!uc)
- return -EINVAL;
+ if (!uc) {
+ ret_val = -EINVAL;
+ goto done;
+ }
- if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0)
- return -EINVAL;
+ if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0) {
+ ret_val = -EINVAL;
+ goto done;
+ }
- if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0)
- return -EINVAL;
+ if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0) {
+ ret_val = -EINVAL;
+ goto done;
+ }
vga_put(pdev, io_state);
@@ -976,7 +1061,6 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
}
} else if (strncmp(curr_pos, "target ", 7) == 0) {
- struct pci_bus *pbus;
unsigned int domain, bus, devfn;
struct vga_device *vgadev;
@@ -995,19 +1079,11 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
pr_debug("vgaarb: %s ==> %x:%x:%x.%x\n", curr_pos,
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
- pbus = pci_find_bus(domain, bus);
- pr_debug("vgaarb: pbus %p\n", pbus);
- if (pbus == NULL) {
- pr_err("vgaarb: invalid PCI domain and/or bus address %x:%x\n",
- domain, bus);
- ret_val = -ENODEV;
- goto done;
- }
- pdev = pci_get_slot(pbus, devfn);
+ pdev = pci_get_domain_bus_and_slot(domain, bus, devfn);
pr_debug("vgaarb: pdev %p\n", pdev);
if (!pdev) {
- pr_err("vgaarb: invalid PCI address %x:%x\n",
- bus, devfn);
+ pr_err("vgaarb: invalid PCI address %x:%x:%x\n",
+ domain, bus, devfn);
ret_val = -ENODEV;
goto done;
}
@@ -1094,10 +1170,9 @@ static int vga_arb_open(struct inode *inode, struct file *file)
pr_debug("%s\n", __func__);
- priv = kmalloc(sizeof(struct vga_arb_private), GFP_KERNEL);
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
- memset(priv, 0, sizeof(*priv));
spin_lock_init(&priv->lock);
file->private_data = priv;
@@ -1222,6 +1297,7 @@ 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)
@@ -1238,6 +1314,13 @@ static int __init vga_arb_device_init(void)
vga_arbiter_add_pci_device(pdev);
pr_info("vgaarb: loaded\n");
+
+ list_for_each_entry(vgadev, &vga_list, list) {
+ if (vgadev->bridge_has_one_vga)
+ pr_info("vgaarb: bridge control possible %s\n", pci_name(vgadev->pdev));
+ else
+ pr_info("vgaarb: no bridge control possible %s\n", pci_name(vgadev->pdev));
+ }
return rc;
}
subsys_initcall(vga_arb_device_init);