diff options
Diffstat (limited to 'drivers/gpu/vga/vgaarb.c')
| -rw-r--r-- | drivers/gpu/vga/vgaarb.c | 221 | 
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);  | 
