162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vgaarb.c: Implements VGA arbitration. For details refer to
462306a36Sopenharmony_ci * Documentation/gpu/vgaarbiter.rst
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (C) Copyright 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
762306a36Sopenharmony_ci * (C) Copyright 2007 Paulo R. Zanoni <przanoni@gmail.com>
862306a36Sopenharmony_ci * (C) Copyright 2007, 2009 Tiago Vignatti <vignatti@freedesktop.org>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) "vgaarb: " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define vgaarb_dbg(dev, fmt, arg...)	dev_dbg(dev, "vgaarb: " fmt, ##arg)
1462306a36Sopenharmony_ci#define vgaarb_info(dev, fmt, arg...)	dev_info(dev, "vgaarb: " fmt, ##arg)
1562306a36Sopenharmony_ci#define vgaarb_err(dev, fmt, arg...)	dev_err(dev, "vgaarb: " fmt, ##arg)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/kernel.h>
1962306a36Sopenharmony_ci#include <linux/pci.h>
2062306a36Sopenharmony_ci#include <linux/errno.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <linux/list.h>
2362306a36Sopenharmony_ci#include <linux/sched/signal.h>
2462306a36Sopenharmony_ci#include <linux/wait.h>
2562306a36Sopenharmony_ci#include <linux/spinlock.h>
2662306a36Sopenharmony_ci#include <linux/poll.h>
2762306a36Sopenharmony_ci#include <linux/miscdevice.h>
2862306a36Sopenharmony_ci#include <linux/slab.h>
2962306a36Sopenharmony_ci#include <linux/screen_info.h>
3062306a36Sopenharmony_ci#include <linux/vt.h>
3162306a36Sopenharmony_ci#include <linux/console.h>
3262306a36Sopenharmony_ci#include <linux/acpi.h>
3362306a36Sopenharmony_ci#include <linux/uaccess.h>
3462306a36Sopenharmony_ci#include <linux/vgaarb.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void vga_arbiter_notify_clients(void);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * We keep a list of all VGA devices in the system to speed
4062306a36Sopenharmony_ci * up the various operations of the arbiter
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistruct vga_device {
4362306a36Sopenharmony_ci	struct list_head list;
4462306a36Sopenharmony_ci	struct pci_dev *pdev;
4562306a36Sopenharmony_ci	unsigned int decodes;		/* what it decodes */
4662306a36Sopenharmony_ci	unsigned int owns;		/* what it owns */
4762306a36Sopenharmony_ci	unsigned int locks;		/* what it locks */
4862306a36Sopenharmony_ci	unsigned int io_lock_cnt;	/* legacy IO lock count */
4962306a36Sopenharmony_ci	unsigned int mem_lock_cnt;	/* legacy MEM lock count */
5062306a36Sopenharmony_ci	unsigned int io_norm_cnt;	/* normal IO count */
5162306a36Sopenharmony_ci	unsigned int mem_norm_cnt;	/* normal MEM count */
5262306a36Sopenharmony_ci	bool bridge_has_one_vga;
5362306a36Sopenharmony_ci	bool is_firmware_default;	/* device selected by firmware */
5462306a36Sopenharmony_ci	unsigned int (*set_decode)(struct pci_dev *pdev, bool decode);
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic LIST_HEAD(vga_list);
5862306a36Sopenharmony_cistatic int vga_count, vga_decode_count;
5962306a36Sopenharmony_cistatic bool vga_arbiter_used;
6062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(vga_lock);
6162306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(vga_wait_queue);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const char *vga_iostate_to_str(unsigned int iostate)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	/* Ignore VGA_RSRC_IO and VGA_RSRC_MEM */
6662306a36Sopenharmony_ci	iostate &= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
6762306a36Sopenharmony_ci	switch (iostate) {
6862306a36Sopenharmony_ci	case VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM:
6962306a36Sopenharmony_ci		return "io+mem";
7062306a36Sopenharmony_ci	case VGA_RSRC_LEGACY_IO:
7162306a36Sopenharmony_ci		return "io";
7262306a36Sopenharmony_ci	case VGA_RSRC_LEGACY_MEM:
7362306a36Sopenharmony_ci		return "mem";
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	return "none";
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int vga_str_to_iostate(char *buf, int str_size, unsigned int *io_state)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * In theory, we could hand out locks on IO and MEM separately to
8262306a36Sopenharmony_ci	 * userspace, but this can cause deadlocks.
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	if (strncmp(buf, "none", 4) == 0) {
8562306a36Sopenharmony_ci		*io_state = VGA_RSRC_NONE;
8662306a36Sopenharmony_ci		return 1;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* XXX We're not checking the str_size! */
9062306a36Sopenharmony_ci	if (strncmp(buf, "io+mem", 6) == 0)
9162306a36Sopenharmony_ci		goto both;
9262306a36Sopenharmony_ci	else if (strncmp(buf, "io", 2) == 0)
9362306a36Sopenharmony_ci		goto both;
9462306a36Sopenharmony_ci	else if (strncmp(buf, "mem", 3) == 0)
9562306a36Sopenharmony_ci		goto both;
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ciboth:
9862306a36Sopenharmony_ci	*io_state = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
9962306a36Sopenharmony_ci	return 1;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* This is only used as a cookie, it should not be dereferenced */
10362306a36Sopenharmony_cistatic struct pci_dev *vga_default;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* Find somebody in our list */
10662306a36Sopenharmony_cistatic struct vga_device *vgadev_find(struct pci_dev *pdev)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct vga_device *vgadev;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	list_for_each_entry(vgadev, &vga_list, list)
11162306a36Sopenharmony_ci		if (pdev == vgadev->pdev)
11262306a36Sopenharmony_ci			return vgadev;
11362306a36Sopenharmony_ci	return NULL;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/**
11762306a36Sopenharmony_ci * vga_default_device - return the default VGA device, for vgacon
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * This can be defined by the platform. The default implementation is
12062306a36Sopenharmony_ci * rather dumb and will probably only work properly on single VGA card
12162306a36Sopenharmony_ci * setups and/or x86 platforms.
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * If your VGA default device is not PCI, you'll have to return NULL here.
12462306a36Sopenharmony_ci * In this case, I assume it will not conflict with any PCI card. If this
12562306a36Sopenharmony_ci * is not true, I'll have to define two arch hooks for enabling/disabling
12662306a36Sopenharmony_ci * the VGA default device if that is possible. This may be a problem with
12762306a36Sopenharmony_ci * real _ISA_ VGA cards, in addition to a PCI one. I don't know at this
12862306a36Sopenharmony_ci * point how to deal with that card. Can their IOs be disabled at all? If
12962306a36Sopenharmony_ci * not, then I suppose it's a matter of having the proper arch hook telling
13062306a36Sopenharmony_ci * us about it, so we basically never allow anybody to succeed a vga_get().
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistruct pci_dev *vga_default_device(void)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	return vga_default;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vga_default_device);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_civoid vga_set_default_device(struct pci_dev *pdev)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	if (vga_default == pdev)
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	pci_dev_put(vga_default);
14462306a36Sopenharmony_ci	vga_default = pci_dev_get(pdev);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/**
14862306a36Sopenharmony_ci * vga_remove_vgacon - deactivate VGA console
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * Unbind and unregister vgacon in case pdev is the default VGA device.
15162306a36Sopenharmony_ci * Can be called by GPU drivers on initialization to make sure VGA register
15262306a36Sopenharmony_ci * access done by vgacon will not disturb the device.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * @pdev: PCI device.
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_ci#if !defined(CONFIG_VGA_CONSOLE)
15762306a36Sopenharmony_ciint vga_remove_vgacon(struct pci_dev *pdev)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci#elif !defined(CONFIG_DUMMY_CONSOLE)
16262306a36Sopenharmony_ciint vga_remove_vgacon(struct pci_dev *pdev)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	return -ENODEV;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci#else
16762306a36Sopenharmony_ciint vga_remove_vgacon(struct pci_dev *pdev)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	int ret = 0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (pdev != vga_default)
17262306a36Sopenharmony_ci		return 0;
17362306a36Sopenharmony_ci	vgaarb_info(&pdev->dev, "deactivate vga console\n");
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	console_lock();
17662306a36Sopenharmony_ci	if (con_is_bound(&vga_con))
17762306a36Sopenharmony_ci		ret = do_take_over_console(&dummy_con, 0,
17862306a36Sopenharmony_ci					   MAX_NR_CONSOLES - 1, 1);
17962306a36Sopenharmony_ci	if (ret == 0) {
18062306a36Sopenharmony_ci		ret = do_unregister_con_driver(&vga_con);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		/* Ignore "already unregistered". */
18362306a36Sopenharmony_ci		if (ret == -ENODEV)
18462306a36Sopenharmony_ci			ret = 0;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	console_unlock();
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return ret;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci#endif
19162306a36Sopenharmony_ciEXPORT_SYMBOL(vga_remove_vgacon);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/*
19462306a36Sopenharmony_ci * If we don't ever use VGA arbitration, we should avoid turning off
19562306a36Sopenharmony_ci * anything anywhere due to old X servers getting confused about the boot
19662306a36Sopenharmony_ci * device not being VGA.
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_cistatic void vga_check_first_use(void)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	/*
20162306a36Sopenharmony_ci	 * Inform all GPUs in the system that VGA arbitration has occurred
20262306a36Sopenharmony_ci	 * so they can disable resources if possible.
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	if (!vga_arbiter_used) {
20562306a36Sopenharmony_ci		vga_arbiter_used = true;
20662306a36Sopenharmony_ci		vga_arbiter_notify_clients();
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic struct vga_device *__vga_tryget(struct vga_device *vgadev,
21162306a36Sopenharmony_ci				       unsigned int rsrc)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct device *dev = &vgadev->pdev->dev;
21462306a36Sopenharmony_ci	unsigned int wants, legacy_wants, match;
21562306a36Sopenharmony_ci	struct vga_device *conflict;
21662306a36Sopenharmony_ci	unsigned int pci_bits;
21762306a36Sopenharmony_ci	u32 flags = 0;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/*
22062306a36Sopenharmony_ci	 * Account for "normal" resources to lock. If we decode the legacy,
22162306a36Sopenharmony_ci	 * counterpart, we need to request it as well
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_IO) &&
22462306a36Sopenharmony_ci	    (vgadev->decodes & VGA_RSRC_LEGACY_IO))
22562306a36Sopenharmony_ci		rsrc |= VGA_RSRC_LEGACY_IO;
22662306a36Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_MEM) &&
22762306a36Sopenharmony_ci	    (vgadev->decodes & VGA_RSRC_LEGACY_MEM))
22862306a36Sopenharmony_ci		rsrc |= VGA_RSRC_LEGACY_MEM;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	vgaarb_dbg(dev, "%s: %d\n", __func__, rsrc);
23162306a36Sopenharmony_ci	vgaarb_dbg(dev, "%s: owns: %d\n", __func__, vgadev->owns);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* Check what resources we need to acquire */
23462306a36Sopenharmony_ci	wants = rsrc & ~vgadev->owns;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* We already own everything, just mark locked & bye bye */
23762306a36Sopenharmony_ci	if (wants == 0)
23862306a36Sopenharmony_ci		goto lock_them;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/*
24162306a36Sopenharmony_ci	 * We don't need to request a legacy resource, we just enable
24262306a36Sopenharmony_ci	 * appropriate decoding and go.
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	legacy_wants = wants & VGA_RSRC_LEGACY_MASK;
24562306a36Sopenharmony_ci	if (legacy_wants == 0)
24662306a36Sopenharmony_ci		goto enable_them;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Ok, we don't, let's find out who we need to kick off */
24962306a36Sopenharmony_ci	list_for_each_entry(conflict, &vga_list, list) {
25062306a36Sopenharmony_ci		unsigned int lwants = legacy_wants;
25162306a36Sopenharmony_ci		unsigned int change_bridge = 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		/* Don't conflict with myself */
25462306a36Sopenharmony_ci		if (vgadev == conflict)
25562306a36Sopenharmony_ci			continue;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		/*
25862306a36Sopenharmony_ci		 * We have a possible conflict. Before we go further, we must
25962306a36Sopenharmony_ci		 * check if we sit on the same bus as the conflicting device.
26062306a36Sopenharmony_ci		 * If we don't, then we must tie both IO and MEM resources
26162306a36Sopenharmony_ci		 * together since there is only a single bit controlling
26262306a36Sopenharmony_ci		 * VGA forwarding on P2P bridges.
26362306a36Sopenharmony_ci		 */
26462306a36Sopenharmony_ci		if (vgadev->pdev->bus != conflict->pdev->bus) {
26562306a36Sopenharmony_ci			change_bridge = 1;
26662306a36Sopenharmony_ci			lwants = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		/*
27062306a36Sopenharmony_ci		 * Check if the guy has a lock on the resource. If he does,
27162306a36Sopenharmony_ci		 * return the conflicting entry.
27262306a36Sopenharmony_ci		 */
27362306a36Sopenharmony_ci		if (conflict->locks & lwants)
27462306a36Sopenharmony_ci			return conflict;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		/*
27762306a36Sopenharmony_ci		 * Ok, now check if it owns the resource we want.  We can
27862306a36Sopenharmony_ci		 * lock resources that are not decoded; therefore a device
27962306a36Sopenharmony_ci		 * can own resources it doesn't decode.
28062306a36Sopenharmony_ci		 */
28162306a36Sopenharmony_ci		match = lwants & conflict->owns;
28262306a36Sopenharmony_ci		if (!match)
28362306a36Sopenharmony_ci			continue;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		/*
28662306a36Sopenharmony_ci		 * Looks like he doesn't have a lock, we can steal them
28762306a36Sopenharmony_ci		 * from him.
28862306a36Sopenharmony_ci		 */
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		flags = 0;
29162306a36Sopenharmony_ci		pci_bits = 0;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/*
29462306a36Sopenharmony_ci		 * If we can't control legacy resources via the bridge, we
29562306a36Sopenharmony_ci		 * also need to disable normal decoding.
29662306a36Sopenharmony_ci		 */
29762306a36Sopenharmony_ci		if (!conflict->bridge_has_one_vga) {
29862306a36Sopenharmony_ci			if ((match & conflict->decodes) & VGA_RSRC_LEGACY_MEM)
29962306a36Sopenharmony_ci				pci_bits |= PCI_COMMAND_MEMORY;
30062306a36Sopenharmony_ci			if ((match & conflict->decodes) & VGA_RSRC_LEGACY_IO)
30162306a36Sopenharmony_ci				pci_bits |= PCI_COMMAND_IO;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci			if (pci_bits)
30462306a36Sopenharmony_ci				flags |= PCI_VGA_STATE_CHANGE_DECODES;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (change_bridge)
30862306a36Sopenharmony_ci			flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		pci_set_vga_state(conflict->pdev, false, pci_bits, flags);
31162306a36Sopenharmony_ci		conflict->owns &= ~match;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		/* If we disabled normal decoding, reflect it in owns */
31462306a36Sopenharmony_ci		if (pci_bits & PCI_COMMAND_MEMORY)
31562306a36Sopenharmony_ci			conflict->owns &= ~VGA_RSRC_NORMAL_MEM;
31662306a36Sopenharmony_ci		if (pci_bits & PCI_COMMAND_IO)
31762306a36Sopenharmony_ci			conflict->owns &= ~VGA_RSRC_NORMAL_IO;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cienable_them:
32162306a36Sopenharmony_ci	/*
32262306a36Sopenharmony_ci	 * Ok, we got it, everybody conflicting has been disabled, let's
32362306a36Sopenharmony_ci	 * enable us.  Mark any bits in "owns" regardless of whether we
32462306a36Sopenharmony_ci	 * decoded them.  We can lock resources we don't decode, therefore
32562306a36Sopenharmony_ci	 * we must track them via "owns".
32662306a36Sopenharmony_ci	 */
32762306a36Sopenharmony_ci	flags = 0;
32862306a36Sopenharmony_ci	pci_bits = 0;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!vgadev->bridge_has_one_vga) {
33162306a36Sopenharmony_ci		flags |= PCI_VGA_STATE_CHANGE_DECODES;
33262306a36Sopenharmony_ci		if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
33362306a36Sopenharmony_ci			pci_bits |= PCI_COMMAND_MEMORY;
33462306a36Sopenharmony_ci		if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
33562306a36Sopenharmony_ci			pci_bits |= PCI_COMMAND_IO;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	if (wants & VGA_RSRC_LEGACY_MASK)
33862306a36Sopenharmony_ci		flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	pci_set_vga_state(vgadev->pdev, true, pci_bits, flags);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	vgadev->owns |= wants;
34362306a36Sopenharmony_cilock_them:
34462306a36Sopenharmony_ci	vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK);
34562306a36Sopenharmony_ci	if (rsrc & VGA_RSRC_LEGACY_IO)
34662306a36Sopenharmony_ci		vgadev->io_lock_cnt++;
34762306a36Sopenharmony_ci	if (rsrc & VGA_RSRC_LEGACY_MEM)
34862306a36Sopenharmony_ci		vgadev->mem_lock_cnt++;
34962306a36Sopenharmony_ci	if (rsrc & VGA_RSRC_NORMAL_IO)
35062306a36Sopenharmony_ci		vgadev->io_norm_cnt++;
35162306a36Sopenharmony_ci	if (rsrc & VGA_RSRC_NORMAL_MEM)
35262306a36Sopenharmony_ci		vgadev->mem_norm_cnt++;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return NULL;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void __vga_put(struct vga_device *vgadev, unsigned int rsrc)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct device *dev = &vgadev->pdev->dev;
36062306a36Sopenharmony_ci	unsigned int old_locks = vgadev->locks;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	vgaarb_dbg(dev, "%s\n", __func__);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/*
36562306a36Sopenharmony_ci	 * Update our counters and account for equivalent legacy resources
36662306a36Sopenharmony_ci	 * if we decode them.
36762306a36Sopenharmony_ci	 */
36862306a36Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_IO) && vgadev->io_norm_cnt > 0) {
36962306a36Sopenharmony_ci		vgadev->io_norm_cnt--;
37062306a36Sopenharmony_ci		if (vgadev->decodes & VGA_RSRC_LEGACY_IO)
37162306a36Sopenharmony_ci			rsrc |= VGA_RSRC_LEGACY_IO;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_MEM) && vgadev->mem_norm_cnt > 0) {
37462306a36Sopenharmony_ci		vgadev->mem_norm_cnt--;
37562306a36Sopenharmony_ci		if (vgadev->decodes & VGA_RSRC_LEGACY_MEM)
37662306a36Sopenharmony_ci			rsrc |= VGA_RSRC_LEGACY_MEM;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	if ((rsrc & VGA_RSRC_LEGACY_IO) && vgadev->io_lock_cnt > 0)
37962306a36Sopenharmony_ci		vgadev->io_lock_cnt--;
38062306a36Sopenharmony_ci	if ((rsrc & VGA_RSRC_LEGACY_MEM) && vgadev->mem_lock_cnt > 0)
38162306a36Sopenharmony_ci		vgadev->mem_lock_cnt--;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/*
38462306a36Sopenharmony_ci	 * Just clear lock bits, we do lazy operations so we don't really
38562306a36Sopenharmony_ci	 * have to bother about anything else at this point.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	if (vgadev->io_lock_cnt == 0)
38862306a36Sopenharmony_ci		vgadev->locks &= ~VGA_RSRC_LEGACY_IO;
38962306a36Sopenharmony_ci	if (vgadev->mem_lock_cnt == 0)
39062306a36Sopenharmony_ci		vgadev->locks &= ~VGA_RSRC_LEGACY_MEM;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/*
39362306a36Sopenharmony_ci	 * Kick the wait queue in case somebody was waiting if we actually
39462306a36Sopenharmony_ci	 * released something.
39562306a36Sopenharmony_ci	 */
39662306a36Sopenharmony_ci	if (old_locks != vgadev->locks)
39762306a36Sopenharmony_ci		wake_up_all(&vga_wait_queue);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/**
40162306a36Sopenharmony_ci * vga_get - acquire & lock VGA resources
40262306a36Sopenharmony_ci * @pdev: PCI device of the VGA card or NULL for the system default
40362306a36Sopenharmony_ci * @rsrc: bit mask of resources to acquire and lock
40462306a36Sopenharmony_ci * @interruptible: blocking should be interruptible by signals ?
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * Acquire VGA resources for the given card and mark those resources
40762306a36Sopenharmony_ci * locked. If the resources requested are "normal" (and not legacy)
40862306a36Sopenharmony_ci * resources, the arbiter will first check whether the card is doing legacy
40962306a36Sopenharmony_ci * decoding for that type of resource. If yes, the lock is "converted" into
41062306a36Sopenharmony_ci * a legacy resource lock.
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * The arbiter will first look for all VGA cards that might conflict and disable
41362306a36Sopenharmony_ci * their IOs and/or Memory access, including VGA forwarding on P2P bridges if
41462306a36Sopenharmony_ci * necessary, so that the requested resources can be used. Then, the card is
41562306a36Sopenharmony_ci * marked as locking these resources and the IO and/or Memory accesses are
41662306a36Sopenharmony_ci * enabled on the card (including VGA forwarding on parent P2P bridges if any).
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci * This function will block if some conflicting card is already locking one of
41962306a36Sopenharmony_ci * the required resources (or any resource on a different bus segment, since P2P
42062306a36Sopenharmony_ci * bridges don't differentiate VGA memory and IO afaik). You can indicate
42162306a36Sopenharmony_ci * whether this blocking should be interruptible by a signal (for userland
42262306a36Sopenharmony_ci * interface) or not.
42362306a36Sopenharmony_ci *
42462306a36Sopenharmony_ci * Must not be called at interrupt time or in atomic context.  If the card
42562306a36Sopenharmony_ci * already owns the resources, the function succeeds.  Nested calls are
42662306a36Sopenharmony_ci * supported (a per-resource counter is maintained)
42762306a36Sopenharmony_ci *
42862306a36Sopenharmony_ci * On success, release the VGA resource again with vga_put().
42962306a36Sopenharmony_ci *
43062306a36Sopenharmony_ci * Returns:
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * 0 on success, negative error code on failure.
43362306a36Sopenharmony_ci */
43462306a36Sopenharmony_ciint vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct vga_device *vgadev, *conflict;
43762306a36Sopenharmony_ci	unsigned long flags;
43862306a36Sopenharmony_ci	wait_queue_entry_t wait;
43962306a36Sopenharmony_ci	int rc = 0;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	vga_check_first_use();
44262306a36Sopenharmony_ci	/* The caller should check for this, but let's be sure */
44362306a36Sopenharmony_ci	if (pdev == NULL)
44462306a36Sopenharmony_ci		pdev = vga_default_device();
44562306a36Sopenharmony_ci	if (pdev == NULL)
44662306a36Sopenharmony_ci		return 0;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	for (;;) {
44962306a36Sopenharmony_ci		spin_lock_irqsave(&vga_lock, flags);
45062306a36Sopenharmony_ci		vgadev = vgadev_find(pdev);
45162306a36Sopenharmony_ci		if (vgadev == NULL) {
45262306a36Sopenharmony_ci			spin_unlock_irqrestore(&vga_lock, flags);
45362306a36Sopenharmony_ci			rc = -ENODEV;
45462306a36Sopenharmony_ci			break;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci		conflict = __vga_tryget(vgadev, rsrc);
45762306a36Sopenharmony_ci		spin_unlock_irqrestore(&vga_lock, flags);
45862306a36Sopenharmony_ci		if (conflict == NULL)
45962306a36Sopenharmony_ci			break;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/*
46262306a36Sopenharmony_ci		 * We have a conflict; we wait until somebody kicks the
46362306a36Sopenharmony_ci		 * work queue. Currently we have one work queue that we
46462306a36Sopenharmony_ci		 * kick each time some resources are released, but it would
46562306a36Sopenharmony_ci		 * be fairly easy to have a per-device one so that we only
46662306a36Sopenharmony_ci		 * need to attach to the conflicting device.
46762306a36Sopenharmony_ci		 */
46862306a36Sopenharmony_ci		init_waitqueue_entry(&wait, current);
46962306a36Sopenharmony_ci		add_wait_queue(&vga_wait_queue, &wait);
47062306a36Sopenharmony_ci		set_current_state(interruptible ?
47162306a36Sopenharmony_ci				  TASK_INTERRUPTIBLE :
47262306a36Sopenharmony_ci				  TASK_UNINTERRUPTIBLE);
47362306a36Sopenharmony_ci		if (interruptible && signal_pending(current)) {
47462306a36Sopenharmony_ci			__set_current_state(TASK_RUNNING);
47562306a36Sopenharmony_ci			remove_wait_queue(&vga_wait_queue, &wait);
47662306a36Sopenharmony_ci			rc = -ERESTARTSYS;
47762306a36Sopenharmony_ci			break;
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci		schedule();
48062306a36Sopenharmony_ci		remove_wait_queue(&vga_wait_queue, &wait);
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	return rc;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ciEXPORT_SYMBOL(vga_get);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/**
48762306a36Sopenharmony_ci * vga_tryget - try to acquire & lock legacy VGA resources
48862306a36Sopenharmony_ci * @pdev: PCI device of VGA card or NULL for system default
48962306a36Sopenharmony_ci * @rsrc: bit mask of resources to acquire and lock
49062306a36Sopenharmony_ci *
49162306a36Sopenharmony_ci * Perform the same operation as vga_get(), but return an error (-EBUSY)
49262306a36Sopenharmony_ci * instead of blocking if the resources are already locked by another card.
49362306a36Sopenharmony_ci * Can be called in any context.
49462306a36Sopenharmony_ci *
49562306a36Sopenharmony_ci * On success, release the VGA resource again with vga_put().
49662306a36Sopenharmony_ci *
49762306a36Sopenharmony_ci * Returns:
49862306a36Sopenharmony_ci *
49962306a36Sopenharmony_ci * 0 on success, negative error code on failure.
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_cistatic int vga_tryget(struct pci_dev *pdev, unsigned int rsrc)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct vga_device *vgadev;
50462306a36Sopenharmony_ci	unsigned long flags;
50562306a36Sopenharmony_ci	int rc = 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	vga_check_first_use();
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* The caller should check for this, but let's be sure */
51062306a36Sopenharmony_ci	if (pdev == NULL)
51162306a36Sopenharmony_ci		pdev = vga_default_device();
51262306a36Sopenharmony_ci	if (pdev == NULL)
51362306a36Sopenharmony_ci		return 0;
51462306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
51562306a36Sopenharmony_ci	vgadev = vgadev_find(pdev);
51662306a36Sopenharmony_ci	if (vgadev == NULL) {
51762306a36Sopenharmony_ci		rc = -ENODEV;
51862306a36Sopenharmony_ci		goto bail;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci	if (__vga_tryget(vgadev, rsrc))
52162306a36Sopenharmony_ci		rc = -EBUSY;
52262306a36Sopenharmony_cibail:
52362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
52462306a36Sopenharmony_ci	return rc;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci/**
52862306a36Sopenharmony_ci * vga_put - release lock on legacy VGA resources
52962306a36Sopenharmony_ci * @pdev: PCI device of VGA card or NULL for system default
53062306a36Sopenharmony_ci * @rsrc: bit mask of resource to release
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci * Release resources previously locked by vga_get() or vga_tryget().  The
53362306a36Sopenharmony_ci * resources aren't disabled right away, so that a subsequent vga_get() on
53462306a36Sopenharmony_ci * the same card will succeed immediately.  Resources have a counter, so
53562306a36Sopenharmony_ci * locks are only released if the counter reaches 0.
53662306a36Sopenharmony_ci */
53762306a36Sopenharmony_civoid vga_put(struct pci_dev *pdev, unsigned int rsrc)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct vga_device *vgadev;
54062306a36Sopenharmony_ci	unsigned long flags;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/* The caller should check for this, but let's be sure */
54362306a36Sopenharmony_ci	if (pdev == NULL)
54462306a36Sopenharmony_ci		pdev = vga_default_device();
54562306a36Sopenharmony_ci	if (pdev == NULL)
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
54862306a36Sopenharmony_ci	vgadev = vgadev_find(pdev);
54962306a36Sopenharmony_ci	if (vgadev == NULL)
55062306a36Sopenharmony_ci		goto bail;
55162306a36Sopenharmony_ci	__vga_put(vgadev, rsrc);
55262306a36Sopenharmony_cibail:
55362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ciEXPORT_SYMBOL(vga_put);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic bool vga_is_firmware_default(struct pci_dev *pdev)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_IA64)
56062306a36Sopenharmony_ci	u64 base = screen_info.lfb_base;
56162306a36Sopenharmony_ci	u64 size = screen_info.lfb_size;
56262306a36Sopenharmony_ci	struct resource *r;
56362306a36Sopenharmony_ci	u64 limit;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/* Select the device owning the boot framebuffer if there is one */
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
56862306a36Sopenharmony_ci		base |= (u64)screen_info.ext_lfb_base << 32;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	limit = base + size;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Does firmware framebuffer belong to us? */
57362306a36Sopenharmony_ci	pci_dev_for_each_resource(pdev, r) {
57462306a36Sopenharmony_ci		if (resource_type(r) != IORESOURCE_MEM)
57562306a36Sopenharmony_ci			continue;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		if (!r->start || !r->end)
57862306a36Sopenharmony_ci			continue;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		if (base < r->start || limit >= r->end)
58162306a36Sopenharmony_ci			continue;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		return true;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci#endif
58662306a36Sopenharmony_ci	return false;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic bool vga_arb_integrated_gpu(struct device *dev)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci#if defined(CONFIG_ACPI)
59262306a36Sopenharmony_ci	struct acpi_device *adev = ACPI_COMPANION(dev);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return adev && !strcmp(acpi_device_hid(adev), ACPI_VIDEO_HID);
59562306a36Sopenharmony_ci#else
59662306a36Sopenharmony_ci	return false;
59762306a36Sopenharmony_ci#endif
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/*
60162306a36Sopenharmony_ci * Return true if vgadev is a better default VGA device than the best one
60262306a36Sopenharmony_ci * we've seen so far.
60362306a36Sopenharmony_ci */
60462306a36Sopenharmony_cistatic bool vga_is_boot_device(struct vga_device *vgadev)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct vga_device *boot_vga = vgadev_find(vga_default_device());
60762306a36Sopenharmony_ci	struct pci_dev *pdev = vgadev->pdev;
60862306a36Sopenharmony_ci	u16 cmd, boot_cmd;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	/*
61162306a36Sopenharmony_ci	 * We select the default VGA device in this order:
61262306a36Sopenharmony_ci	 *   Firmware framebuffer (see vga_arb_select_default_device())
61362306a36Sopenharmony_ci	 *   Legacy VGA device (owns VGA_RSRC_LEGACY_MASK)
61462306a36Sopenharmony_ci	 *   Non-legacy integrated device (see vga_arb_select_default_device())
61562306a36Sopenharmony_ci	 *   Non-legacy discrete device (see vga_arb_select_default_device())
61662306a36Sopenharmony_ci	 *   Other device (see vga_arb_select_default_device())
61762306a36Sopenharmony_ci	 */
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/*
62062306a36Sopenharmony_ci	 * We always prefer a firmware default device, so if we've already
62162306a36Sopenharmony_ci	 * found one, there's no need to consider vgadev.
62262306a36Sopenharmony_ci	 */
62362306a36Sopenharmony_ci	if (boot_vga && boot_vga->is_firmware_default)
62462306a36Sopenharmony_ci		return false;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (vga_is_firmware_default(pdev)) {
62762306a36Sopenharmony_ci		vgadev->is_firmware_default = true;
62862306a36Sopenharmony_ci		return true;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/*
63262306a36Sopenharmony_ci	 * A legacy VGA device has MEM and IO enabled and any bridges
63362306a36Sopenharmony_ci	 * leading to it have PCI_BRIDGE_CTL_VGA enabled so the legacy
63462306a36Sopenharmony_ci	 * resources ([mem 0xa0000-0xbffff], [io 0x3b0-0x3bb], etc) are
63562306a36Sopenharmony_ci	 * routed to it.
63662306a36Sopenharmony_ci	 *
63762306a36Sopenharmony_ci	 * We use the first one we find, so if we've already found one,
63862306a36Sopenharmony_ci	 * vgadev is no better.
63962306a36Sopenharmony_ci	 */
64062306a36Sopenharmony_ci	if (boot_vga &&
64162306a36Sopenharmony_ci	    (boot_vga->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)
64262306a36Sopenharmony_ci		return false;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)
64562306a36Sopenharmony_ci		return true;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/*
64862306a36Sopenharmony_ci	 * If we haven't found a legacy VGA device, accept a non-legacy
64962306a36Sopenharmony_ci	 * device.  It may have either IO or MEM enabled, and bridges may
65062306a36Sopenharmony_ci	 * not have PCI_BRIDGE_CTL_VGA enabled, so it may not be able to
65162306a36Sopenharmony_ci	 * use legacy VGA resources.  Prefer an integrated GPU over others.
65262306a36Sopenharmony_ci	 */
65362306a36Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
65462306a36Sopenharmony_ci	if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		/*
65762306a36Sopenharmony_ci		 * An integrated GPU overrides a previous non-legacy
65862306a36Sopenharmony_ci		 * device.  We expect only a single integrated GPU, but if
65962306a36Sopenharmony_ci		 * there are more, we use the *last* because that was the
66062306a36Sopenharmony_ci		 * previous behavior.
66162306a36Sopenharmony_ci		 */
66262306a36Sopenharmony_ci		if (vga_arb_integrated_gpu(&pdev->dev))
66362306a36Sopenharmony_ci			return true;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		/*
66662306a36Sopenharmony_ci		 * We prefer the first non-legacy discrete device we find.
66762306a36Sopenharmony_ci		 * If we already found one, vgadev is no better.
66862306a36Sopenharmony_ci		 */
66962306a36Sopenharmony_ci		if (boot_vga) {
67062306a36Sopenharmony_ci			pci_read_config_word(boot_vga->pdev, PCI_COMMAND,
67162306a36Sopenharmony_ci					     &boot_cmd);
67262306a36Sopenharmony_ci			if (boot_cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))
67362306a36Sopenharmony_ci				return false;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci		return true;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/*
67962306a36Sopenharmony_ci	 * Vgadev has neither IO nor MEM enabled.  If we haven't found any
68062306a36Sopenharmony_ci	 * other VGA devices, it is the best candidate so far.
68162306a36Sopenharmony_ci	 */
68262306a36Sopenharmony_ci	if (!boot_vga)
68362306a36Sopenharmony_ci		return true;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return false;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci/*
68962306a36Sopenharmony_ci * Rules for using a bridge to control a VGA descendant decoding: if a bridge
69062306a36Sopenharmony_ci * has only one VGA descendant then it can be used to control the VGA routing
69162306a36Sopenharmony_ci * for that device. It should always use the bridge closest to the device to
69262306a36Sopenharmony_ci * control it. If a bridge has a direct VGA descendant, but also have a sub-
69362306a36Sopenharmony_ci * bridge VGA descendant then we cannot use that bridge to control the direct
69462306a36Sopenharmony_ci * VGA descendant. So for every device we register, we need to iterate all
69562306a36Sopenharmony_ci * its parent bridges so we can invalidate any devices using them properly.
69662306a36Sopenharmony_ci */
69762306a36Sopenharmony_cistatic void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct vga_device *same_bridge_vgadev;
70062306a36Sopenharmony_ci	struct pci_bus *new_bus, *bus;
70162306a36Sopenharmony_ci	struct pci_dev *new_bridge, *bridge;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	vgadev->bridge_has_one_vga = true;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (list_empty(&vga_list)) {
70662306a36Sopenharmony_ci		vgaarb_info(&vgadev->pdev->dev, "bridge control possible\n");
70762306a36Sopenharmony_ci		return;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* Iterate the new device's bridge hierarchy */
71162306a36Sopenharmony_ci	new_bus = vgadev->pdev->bus;
71262306a36Sopenharmony_ci	while (new_bus) {
71362306a36Sopenharmony_ci		new_bridge = new_bus->self;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		/* Go through list of devices already registered */
71662306a36Sopenharmony_ci		list_for_each_entry(same_bridge_vgadev, &vga_list, list) {
71762306a36Sopenharmony_ci			bus = same_bridge_vgadev->pdev->bus;
71862306a36Sopenharmony_ci			bridge = bus->self;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci			/* See if it shares a bridge with this device */
72162306a36Sopenharmony_ci			if (new_bridge == bridge) {
72262306a36Sopenharmony_ci				/*
72362306a36Sopenharmony_ci				 * If its direct parent bridge is the same
72462306a36Sopenharmony_ci				 * as any bridge of this device then it can't
72562306a36Sopenharmony_ci				 * be used for that device.
72662306a36Sopenharmony_ci				 */
72762306a36Sopenharmony_ci				same_bridge_vgadev->bridge_has_one_vga = false;
72862306a36Sopenharmony_ci			}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci			/*
73162306a36Sopenharmony_ci			 * Now iterate the previous device's bridge hierarchy.
73262306a36Sopenharmony_ci			 * If the new device's parent bridge is in the other
73362306a36Sopenharmony_ci			 * device's hierarchy, we can't use it to control this
73462306a36Sopenharmony_ci			 * device.
73562306a36Sopenharmony_ci			 */
73662306a36Sopenharmony_ci			while (bus) {
73762306a36Sopenharmony_ci				bridge = bus->self;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci				if (bridge && bridge == vgadev->pdev->bus->self)
74062306a36Sopenharmony_ci					vgadev->bridge_has_one_vga = false;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci				bus = bus->parent;
74362306a36Sopenharmony_ci			}
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci		new_bus = new_bus->parent;
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (vgadev->bridge_has_one_vga)
74962306a36Sopenharmony_ci		vgaarb_info(&vgadev->pdev->dev, "bridge control possible\n");
75062306a36Sopenharmony_ci	else
75162306a36Sopenharmony_ci		vgaarb_info(&vgadev->pdev->dev, "no bridge control possible\n");
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci/*
75562306a36Sopenharmony_ci * Currently, we assume that the "initial" setup of the system is not sane,
75662306a36Sopenharmony_ci * that is, we come up with conflicting devices and let the arbiter's
75762306a36Sopenharmony_ci * client decide if devices decodes legacy things or not.
75862306a36Sopenharmony_ci */
75962306a36Sopenharmony_cistatic bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct vga_device *vgadev;
76262306a36Sopenharmony_ci	unsigned long flags;
76362306a36Sopenharmony_ci	struct pci_bus *bus;
76462306a36Sopenharmony_ci	struct pci_dev *bridge;
76562306a36Sopenharmony_ci	u16 cmd;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* Only deal with VGA class devices */
76862306a36Sopenharmony_ci	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
76962306a36Sopenharmony_ci		return false;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* Allocate structure */
77262306a36Sopenharmony_ci	vgadev = kzalloc(sizeof(struct vga_device), GFP_KERNEL);
77362306a36Sopenharmony_ci	if (vgadev == NULL) {
77462306a36Sopenharmony_ci		vgaarb_err(&pdev->dev, "failed to allocate VGA arbiter data\n");
77562306a36Sopenharmony_ci		/*
77662306a36Sopenharmony_ci		 * What to do on allocation failure? For now, let's just do
77762306a36Sopenharmony_ci		 * nothing, I'm not sure there is anything saner to be done.
77862306a36Sopenharmony_ci		 */
77962306a36Sopenharmony_ci		return false;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* Take lock & check for duplicates */
78362306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
78462306a36Sopenharmony_ci	if (vgadev_find(pdev) != NULL) {
78562306a36Sopenharmony_ci		BUG_ON(1);
78662306a36Sopenharmony_ci		goto fail;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci	vgadev->pdev = pdev;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	/* By default, assume we decode everything */
79162306a36Sopenharmony_ci	vgadev->decodes = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
79262306a36Sopenharmony_ci			  VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* By default, mark it as decoding */
79562306a36Sopenharmony_ci	vga_decode_count++;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/*
79862306a36Sopenharmony_ci	 * Mark that we "own" resources based on our enables, we will
79962306a36Sopenharmony_ci	 * clear that below if the bridge isn't forwarding.
80062306a36Sopenharmony_ci	 */
80162306a36Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
80262306a36Sopenharmony_ci	if (cmd & PCI_COMMAND_IO)
80362306a36Sopenharmony_ci		vgadev->owns |= VGA_RSRC_LEGACY_IO;
80462306a36Sopenharmony_ci	if (cmd & PCI_COMMAND_MEMORY)
80562306a36Sopenharmony_ci		vgadev->owns |= VGA_RSRC_LEGACY_MEM;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/* Check if VGA cycles can get down to us */
80862306a36Sopenharmony_ci	bus = pdev->bus;
80962306a36Sopenharmony_ci	while (bus) {
81062306a36Sopenharmony_ci		bridge = bus->self;
81162306a36Sopenharmony_ci		if (bridge) {
81262306a36Sopenharmony_ci			u16 l;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
81562306a36Sopenharmony_ci			if (!(l & PCI_BRIDGE_CTL_VGA)) {
81662306a36Sopenharmony_ci				vgadev->owns = 0;
81762306a36Sopenharmony_ci				break;
81862306a36Sopenharmony_ci			}
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci		bus = bus->parent;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	if (vga_is_boot_device(vgadev)) {
82462306a36Sopenharmony_ci		vgaarb_info(&pdev->dev, "setting as boot VGA device%s\n",
82562306a36Sopenharmony_ci			    vga_default_device() ?
82662306a36Sopenharmony_ci			    " (overriding previous)" : "");
82762306a36Sopenharmony_ci		vga_set_default_device(pdev);
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	vga_arbiter_check_bridge_sharing(vgadev);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/* Add to the list */
83362306a36Sopenharmony_ci	list_add_tail(&vgadev->list, &vga_list);
83462306a36Sopenharmony_ci	vga_count++;
83562306a36Sopenharmony_ci	vgaarb_info(&pdev->dev, "VGA device added: decodes=%s,owns=%s,locks=%s\n",
83662306a36Sopenharmony_ci		vga_iostate_to_str(vgadev->decodes),
83762306a36Sopenharmony_ci		vga_iostate_to_str(vgadev->owns),
83862306a36Sopenharmony_ci		vga_iostate_to_str(vgadev->locks));
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
84162306a36Sopenharmony_ci	return true;
84262306a36Sopenharmony_cifail:
84362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
84462306a36Sopenharmony_ci	kfree(vgadev);
84562306a36Sopenharmony_ci	return false;
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_cistatic bool vga_arbiter_del_pci_device(struct pci_dev *pdev)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	struct vga_device *vgadev;
85162306a36Sopenharmony_ci	unsigned long flags;
85262306a36Sopenharmony_ci	bool ret = true;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
85562306a36Sopenharmony_ci	vgadev = vgadev_find(pdev);
85662306a36Sopenharmony_ci	if (vgadev == NULL) {
85762306a36Sopenharmony_ci		ret = false;
85862306a36Sopenharmony_ci		goto bail;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	if (vga_default == pdev)
86262306a36Sopenharmony_ci		vga_set_default_device(NULL);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM))
86562306a36Sopenharmony_ci		vga_decode_count--;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	/* Remove entry from list */
86862306a36Sopenharmony_ci	list_del(&vgadev->list);
86962306a36Sopenharmony_ci	vga_count--;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* Wake up all possible waiters */
87262306a36Sopenharmony_ci	wake_up_all(&vga_wait_queue);
87362306a36Sopenharmony_cibail:
87462306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
87562306a36Sopenharmony_ci	kfree(vgadev);
87662306a36Sopenharmony_ci	return ret;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci/* Called with the lock */
88062306a36Sopenharmony_cistatic void vga_update_device_decodes(struct vga_device *vgadev,
88162306a36Sopenharmony_ci				      unsigned int new_decodes)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct device *dev = &vgadev->pdev->dev;
88462306a36Sopenharmony_ci	unsigned int old_decodes = vgadev->decodes;
88562306a36Sopenharmony_ci	unsigned int decodes_removed = ~new_decodes & old_decodes;
88662306a36Sopenharmony_ci	unsigned int decodes_unlocked = vgadev->locks & decodes_removed;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	vgadev->decodes = new_decodes;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	vgaarb_info(dev, "VGA decodes changed: olddecodes=%s,decodes=%s:owns=%s\n",
89162306a36Sopenharmony_ci		    vga_iostate_to_str(old_decodes),
89262306a36Sopenharmony_ci		    vga_iostate_to_str(vgadev->decodes),
89362306a36Sopenharmony_ci		    vga_iostate_to_str(vgadev->owns));
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/* If we removed locked decodes, lock count goes to zero, and release */
89662306a36Sopenharmony_ci	if (decodes_unlocked) {
89762306a36Sopenharmony_ci		if (decodes_unlocked & VGA_RSRC_LEGACY_IO)
89862306a36Sopenharmony_ci			vgadev->io_lock_cnt = 0;
89962306a36Sopenharmony_ci		if (decodes_unlocked & VGA_RSRC_LEGACY_MEM)
90062306a36Sopenharmony_ci			vgadev->mem_lock_cnt = 0;
90162306a36Sopenharmony_ci		__vga_put(vgadev, decodes_unlocked);
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/* Change decodes counter */
90562306a36Sopenharmony_ci	if (old_decodes & VGA_RSRC_LEGACY_MASK &&
90662306a36Sopenharmony_ci	    !(new_decodes & VGA_RSRC_LEGACY_MASK))
90762306a36Sopenharmony_ci		vga_decode_count--;
90862306a36Sopenharmony_ci	if (!(old_decodes & VGA_RSRC_LEGACY_MASK) &&
90962306a36Sopenharmony_ci	    new_decodes & VGA_RSRC_LEGACY_MASK)
91062306a36Sopenharmony_ci		vga_decode_count++;
91162306a36Sopenharmony_ci	vgaarb_dbg(dev, "decoding count now is: %d\n", vga_decode_count);
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic void __vga_set_legacy_decoding(struct pci_dev *pdev,
91562306a36Sopenharmony_ci				      unsigned int decodes,
91662306a36Sopenharmony_ci				      bool userspace)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct vga_device *vgadev;
91962306a36Sopenharmony_ci	unsigned long flags;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	decodes &= VGA_RSRC_LEGACY_MASK;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
92462306a36Sopenharmony_ci	vgadev = vgadev_find(pdev);
92562306a36Sopenharmony_ci	if (vgadev == NULL)
92662306a36Sopenharmony_ci		goto bail;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* Don't let userspace futz with kernel driver decodes */
92962306a36Sopenharmony_ci	if (userspace && vgadev->set_decode)
93062306a36Sopenharmony_ci		goto bail;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* Update the device decodes + counter */
93362306a36Sopenharmony_ci	vga_update_device_decodes(vgadev, decodes);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/*
93662306a36Sopenharmony_ci	 * XXX If somebody is going from "doesn't decode" to "decodes"
93762306a36Sopenharmony_ci	 * state here, additional care must be taken as we may have pending
93862306a36Sopenharmony_ci	 * ownership of non-legacy region.
93962306a36Sopenharmony_ci	 */
94062306a36Sopenharmony_cibail:
94162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci/**
94562306a36Sopenharmony_ci * vga_set_legacy_decoding
94662306a36Sopenharmony_ci * @pdev: PCI device of the VGA card
94762306a36Sopenharmony_ci * @decodes: bit mask of what legacy regions the card decodes
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * Indicate to the arbiter if the card decodes legacy VGA IOs, legacy VGA
95062306a36Sopenharmony_ci * Memory, both, or none. All cards default to both, the card driver (fbdev for
95162306a36Sopenharmony_ci * example) should tell the arbiter if it has disabled legacy decoding, so the
95262306a36Sopenharmony_ci * card can be left out of the arbitration process (and can be safe to take
95362306a36Sopenharmony_ci * interrupts at any time.
95462306a36Sopenharmony_ci */
95562306a36Sopenharmony_civoid vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	__vga_set_legacy_decoding(pdev, decodes, false);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ciEXPORT_SYMBOL(vga_set_legacy_decoding);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci/**
96262306a36Sopenharmony_ci * vga_client_register - register or unregister a VGA arbitration client
96362306a36Sopenharmony_ci * @pdev: PCI device of the VGA client
96462306a36Sopenharmony_ci * @set_decode: VGA decode change callback
96562306a36Sopenharmony_ci *
96662306a36Sopenharmony_ci * Clients have two callback mechanisms they can use.
96762306a36Sopenharmony_ci *
96862306a36Sopenharmony_ci * @set_decode callback: If a client can disable its GPU VGA resource, it
96962306a36Sopenharmony_ci * will get a callback from this to set the encode/decode state.
97062306a36Sopenharmony_ci *
97162306a36Sopenharmony_ci * Rationale: we cannot disable VGA decode resources unconditionally
97262306a36Sopenharmony_ci * because some single GPU laptops seem to require ACPI or BIOS access to
97362306a36Sopenharmony_ci * the VGA registers to control things like backlights etc. Hopefully newer
97462306a36Sopenharmony_ci * multi-GPU laptops do something saner, and desktops won't have any
97562306a36Sopenharmony_ci * special ACPI for this. The driver will get a callback when VGA
97662306a36Sopenharmony_ci * arbitration is first used by userspace since some older X servers have
97762306a36Sopenharmony_ci * issues.
97862306a36Sopenharmony_ci *
97962306a36Sopenharmony_ci * Does not check whether a client for @pdev has been registered already.
98062306a36Sopenharmony_ci *
98162306a36Sopenharmony_ci * To unregister, call vga_client_unregister().
98262306a36Sopenharmony_ci *
98362306a36Sopenharmony_ci * Returns: 0 on success, -ENODEV on failure
98462306a36Sopenharmony_ci */
98562306a36Sopenharmony_ciint vga_client_register(struct pci_dev *pdev,
98662306a36Sopenharmony_ci		unsigned int (*set_decode)(struct pci_dev *pdev, bool decode))
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	unsigned long flags;
98962306a36Sopenharmony_ci	struct vga_device *vgadev;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
99262306a36Sopenharmony_ci	vgadev = vgadev_find(pdev);
99362306a36Sopenharmony_ci	if (vgadev)
99462306a36Sopenharmony_ci		vgadev->set_decode = set_decode;
99562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
99662306a36Sopenharmony_ci	if (!vgadev)
99762306a36Sopenharmony_ci		return -ENODEV;
99862306a36Sopenharmony_ci	return 0;
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ciEXPORT_SYMBOL(vga_client_register);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci/*
100362306a36Sopenharmony_ci * Char driver implementation
100462306a36Sopenharmony_ci *
100562306a36Sopenharmony_ci * Semantics is:
100662306a36Sopenharmony_ci *
100762306a36Sopenharmony_ci *  open       : Open user instance of the arbiter. By default, it's
100862306a36Sopenharmony_ci *                attached to the default VGA device of the system.
100962306a36Sopenharmony_ci *
101062306a36Sopenharmony_ci *  close      : Close user instance, release locks
101162306a36Sopenharmony_ci *
101262306a36Sopenharmony_ci *  read       : Return a string indicating the status of the target.
101362306a36Sopenharmony_ci *                An IO state string is of the form {io,mem,io+mem,none},
101462306a36Sopenharmony_ci *                mc and ic are respectively mem and io lock counts (for
101562306a36Sopenharmony_ci *                debugging/diagnostic only). "decodes" indicate what the
101662306a36Sopenharmony_ci *                card currently decodes, "owns" indicates what is currently
101762306a36Sopenharmony_ci *                enabled on it, and "locks" indicates what is locked by this
101862306a36Sopenharmony_ci *                card. If the card is unplugged, we get "invalid" then for
101962306a36Sopenharmony_ci *                card_ID and an -ENODEV error is returned for any command
102062306a36Sopenharmony_ci *                until a new card is targeted
102162306a36Sopenharmony_ci *
102262306a36Sopenharmony_ci *   "<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)"
102362306a36Sopenharmony_ci *
102462306a36Sopenharmony_ci * write       : write a command to the arbiter. List of commands is:
102562306a36Sopenharmony_ci *
102662306a36Sopenharmony_ci *   target <card_ID>   : switch target to card <card_ID> (see below)
102762306a36Sopenharmony_ci *   lock <io_state>    : acquire locks on target ("none" is invalid io_state)
102862306a36Sopenharmony_ci *   trylock <io_state> : non-blocking acquire locks on target
102962306a36Sopenharmony_ci *   unlock <io_state>  : release locks on target
103062306a36Sopenharmony_ci *   unlock all         : release all locks on target held by this user
103162306a36Sopenharmony_ci *   decodes <io_state> : set the legacy decoding attributes for the card
103262306a36Sopenharmony_ci *
103362306a36Sopenharmony_ci * poll         : event if something change on any card (not just the target)
103462306a36Sopenharmony_ci *
103562306a36Sopenharmony_ci * card_ID is of the form "PCI:domain:bus:dev.fn". It can be set to "default"
103662306a36Sopenharmony_ci * to go back to the system default card (TODO: not implemented yet).
103762306a36Sopenharmony_ci * Currently, only PCI is supported as a prefix, but the userland API may
103862306a36Sopenharmony_ci * support other bus types in the future, even if the current kernel
103962306a36Sopenharmony_ci * implementation doesn't.
104062306a36Sopenharmony_ci *
104162306a36Sopenharmony_ci * Note about locks:
104262306a36Sopenharmony_ci *
104362306a36Sopenharmony_ci * The driver keeps track of which user has what locks on which card. It
104462306a36Sopenharmony_ci * supports stacking, like the kernel one. This complicates the implementation
104562306a36Sopenharmony_ci * a bit, but makes the arbiter more tolerant to userspace problems and able
104662306a36Sopenharmony_ci * to properly cleanup in all cases when a process dies.
104762306a36Sopenharmony_ci * Currently, a max of 16 cards simultaneously can have locks issued from
104862306a36Sopenharmony_ci * userspace for a given user (file descriptor instance) of the arbiter.
104962306a36Sopenharmony_ci *
105062306a36Sopenharmony_ci * If the device is hot-unplugged, there is a hook inside the module to notify
105162306a36Sopenharmony_ci * it being added/removed in the system and automatically added/removed in
105262306a36Sopenharmony_ci * the arbiter.
105362306a36Sopenharmony_ci */
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci#define MAX_USER_CARDS         CONFIG_VGA_ARB_MAX_GPUS
105662306a36Sopenharmony_ci#define PCI_INVALID_CARD       ((struct pci_dev *)-1UL)
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci/* Each user has an array of these, tracking which cards have locks */
105962306a36Sopenharmony_cistruct vga_arb_user_card {
106062306a36Sopenharmony_ci	struct pci_dev *pdev;
106162306a36Sopenharmony_ci	unsigned int mem_cnt;
106262306a36Sopenharmony_ci	unsigned int io_cnt;
106362306a36Sopenharmony_ci};
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistruct vga_arb_private {
106662306a36Sopenharmony_ci	struct list_head list;
106762306a36Sopenharmony_ci	struct pci_dev *target;
106862306a36Sopenharmony_ci	struct vga_arb_user_card cards[MAX_USER_CARDS];
106962306a36Sopenharmony_ci	spinlock_t lock;
107062306a36Sopenharmony_ci};
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_cistatic LIST_HEAD(vga_user_list);
107362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(vga_user_lock);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci/*
107762306a36Sopenharmony_ci * Take a string in the format: "PCI:domain:bus:dev.fn" and return the
107862306a36Sopenharmony_ci * respective values. If the string is not in this format, return 0.
107962306a36Sopenharmony_ci */
108062306a36Sopenharmony_cistatic int vga_pci_str_to_vars(char *buf, int count, unsigned int *domain,
108162306a36Sopenharmony_ci			       unsigned int *bus, unsigned int *devfn)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	int n;
108462306a36Sopenharmony_ci	unsigned int slot, func;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	n = sscanf(buf, "PCI:%x:%x:%x.%x", domain, bus, &slot, &func);
108762306a36Sopenharmony_ci	if (n != 4)
108862306a36Sopenharmony_ci		return 0;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	*devfn = PCI_DEVFN(slot, func);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	return 1;
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_cistatic ssize_t vga_arb_read(struct file *file, char __user *buf,
109662306a36Sopenharmony_ci			    size_t count, loff_t *ppos)
109762306a36Sopenharmony_ci{
109862306a36Sopenharmony_ci	struct vga_arb_private *priv = file->private_data;
109962306a36Sopenharmony_ci	struct vga_device *vgadev;
110062306a36Sopenharmony_ci	struct pci_dev *pdev;
110162306a36Sopenharmony_ci	unsigned long flags;
110262306a36Sopenharmony_ci	size_t len;
110362306a36Sopenharmony_ci	int rc;
110462306a36Sopenharmony_ci	char *lbuf;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	lbuf = kmalloc(1024, GFP_KERNEL);
110762306a36Sopenharmony_ci	if (lbuf == NULL)
110862306a36Sopenharmony_ci		return -ENOMEM;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	/* Protect vga_list */
111162306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* If we are targeting the default, use it */
111462306a36Sopenharmony_ci	pdev = priv->target;
111562306a36Sopenharmony_ci	if (pdev == NULL || pdev == PCI_INVALID_CARD) {
111662306a36Sopenharmony_ci		spin_unlock_irqrestore(&vga_lock, flags);
111762306a36Sopenharmony_ci		len = sprintf(lbuf, "invalid");
111862306a36Sopenharmony_ci		goto done;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	/* Find card vgadev structure */
112262306a36Sopenharmony_ci	vgadev = vgadev_find(pdev);
112362306a36Sopenharmony_ci	if (vgadev == NULL) {
112462306a36Sopenharmony_ci		/*
112562306a36Sopenharmony_ci		 * Wow, it's not in the list, that shouldn't happen, let's
112662306a36Sopenharmony_ci		 * fix us up and return invalid card.
112762306a36Sopenharmony_ci		 */
112862306a36Sopenharmony_ci		spin_unlock_irqrestore(&vga_lock, flags);
112962306a36Sopenharmony_ci		len = sprintf(lbuf, "invalid");
113062306a36Sopenharmony_ci		goto done;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	/* Fill the buffer with info */
113462306a36Sopenharmony_ci	len = snprintf(lbuf, 1024,
113562306a36Sopenharmony_ci		       "count:%d,PCI:%s,decodes=%s,owns=%s,locks=%s(%u:%u)\n",
113662306a36Sopenharmony_ci		       vga_decode_count, pci_name(pdev),
113762306a36Sopenharmony_ci		       vga_iostate_to_str(vgadev->decodes),
113862306a36Sopenharmony_ci		       vga_iostate_to_str(vgadev->owns),
113962306a36Sopenharmony_ci		       vga_iostate_to_str(vgadev->locks),
114062306a36Sopenharmony_ci		       vgadev->io_lock_cnt, vgadev->mem_lock_cnt);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
114362306a36Sopenharmony_cidone:
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/* Copy that to user */
114662306a36Sopenharmony_ci	if (len > count)
114762306a36Sopenharmony_ci		len = count;
114862306a36Sopenharmony_ci	rc = copy_to_user(buf, lbuf, len);
114962306a36Sopenharmony_ci	kfree(lbuf);
115062306a36Sopenharmony_ci	if (rc)
115162306a36Sopenharmony_ci		return -EFAULT;
115262306a36Sopenharmony_ci	return len;
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci/*
115662306a36Sopenharmony_ci * TODO: To avoid parsing inside kernel and to improve the speed we may
115762306a36Sopenharmony_ci * consider use ioctl here
115862306a36Sopenharmony_ci */
115962306a36Sopenharmony_cistatic ssize_t vga_arb_write(struct file *file, const char __user *buf,
116062306a36Sopenharmony_ci			     size_t count, loff_t *ppos)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct vga_arb_private *priv = file->private_data;
116362306a36Sopenharmony_ci	struct vga_arb_user_card *uc = NULL;
116462306a36Sopenharmony_ci	struct pci_dev *pdev;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	unsigned int io_state;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	char kbuf[64], *curr_pos;
116962306a36Sopenharmony_ci	size_t remaining = count;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	int ret_val;
117262306a36Sopenharmony_ci	int i;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (count >= sizeof(kbuf))
117562306a36Sopenharmony_ci		return -EINVAL;
117662306a36Sopenharmony_ci	if (copy_from_user(kbuf, buf, count))
117762306a36Sopenharmony_ci		return -EFAULT;
117862306a36Sopenharmony_ci	curr_pos = kbuf;
117962306a36Sopenharmony_ci	kbuf[count] = '\0';
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if (strncmp(curr_pos, "lock ", 5) == 0) {
118262306a36Sopenharmony_ci		curr_pos += 5;
118362306a36Sopenharmony_ci		remaining -= 5;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci		pr_debug("client 0x%p called 'lock'\n", priv);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) {
118862306a36Sopenharmony_ci			ret_val = -EPROTO;
118962306a36Sopenharmony_ci			goto done;
119062306a36Sopenharmony_ci		}
119162306a36Sopenharmony_ci		if (io_state == VGA_RSRC_NONE) {
119262306a36Sopenharmony_ci			ret_val = -EPROTO;
119362306a36Sopenharmony_ci			goto done;
119462306a36Sopenharmony_ci		}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		pdev = priv->target;
119762306a36Sopenharmony_ci		if (priv->target == NULL) {
119862306a36Sopenharmony_ci			ret_val = -ENODEV;
119962306a36Sopenharmony_ci			goto done;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		vga_get_uninterruptible(pdev, io_state);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		/* Update the client's locks lists */
120562306a36Sopenharmony_ci		for (i = 0; i < MAX_USER_CARDS; i++) {
120662306a36Sopenharmony_ci			if (priv->cards[i].pdev == pdev) {
120762306a36Sopenharmony_ci				if (io_state & VGA_RSRC_LEGACY_IO)
120862306a36Sopenharmony_ci					priv->cards[i].io_cnt++;
120962306a36Sopenharmony_ci				if (io_state & VGA_RSRC_LEGACY_MEM)
121062306a36Sopenharmony_ci					priv->cards[i].mem_cnt++;
121162306a36Sopenharmony_ci				break;
121262306a36Sopenharmony_ci			}
121362306a36Sopenharmony_ci		}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci		ret_val = count;
121662306a36Sopenharmony_ci		goto done;
121762306a36Sopenharmony_ci	} else if (strncmp(curr_pos, "unlock ", 7) == 0) {
121862306a36Sopenharmony_ci		curr_pos += 7;
121962306a36Sopenharmony_ci		remaining -= 7;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci		pr_debug("client 0x%p called 'unlock'\n", priv);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		if (strncmp(curr_pos, "all", 3) == 0)
122462306a36Sopenharmony_ci			io_state = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
122562306a36Sopenharmony_ci		else {
122662306a36Sopenharmony_ci			if (!vga_str_to_iostate
122762306a36Sopenharmony_ci			    (curr_pos, remaining, &io_state)) {
122862306a36Sopenharmony_ci				ret_val = -EPROTO;
122962306a36Sopenharmony_ci				goto done;
123062306a36Sopenharmony_ci			}
123162306a36Sopenharmony_ci			/* TODO: Add this?
123262306a36Sopenharmony_ci			   if (io_state == VGA_RSRC_NONE) {
123362306a36Sopenharmony_ci			   ret_val = -EPROTO;
123462306a36Sopenharmony_ci			   goto done;
123562306a36Sopenharmony_ci			   }
123662306a36Sopenharmony_ci			  */
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		pdev = priv->target;
124062306a36Sopenharmony_ci		if (priv->target == NULL) {
124162306a36Sopenharmony_ci			ret_val = -ENODEV;
124262306a36Sopenharmony_ci			goto done;
124362306a36Sopenharmony_ci		}
124462306a36Sopenharmony_ci		for (i = 0; i < MAX_USER_CARDS; i++) {
124562306a36Sopenharmony_ci			if (priv->cards[i].pdev == pdev)
124662306a36Sopenharmony_ci				uc = &priv->cards[i];
124762306a36Sopenharmony_ci		}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		if (!uc) {
125062306a36Sopenharmony_ci			ret_val = -EINVAL;
125162306a36Sopenharmony_ci			goto done;
125262306a36Sopenharmony_ci		}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0) {
125562306a36Sopenharmony_ci			ret_val = -EINVAL;
125662306a36Sopenharmony_ci			goto done;
125762306a36Sopenharmony_ci		}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0) {
126062306a36Sopenharmony_ci			ret_val = -EINVAL;
126162306a36Sopenharmony_ci			goto done;
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci		vga_put(pdev, io_state);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_IO)
126762306a36Sopenharmony_ci			uc->io_cnt--;
126862306a36Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_MEM)
126962306a36Sopenharmony_ci			uc->mem_cnt--;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci		ret_val = count;
127262306a36Sopenharmony_ci		goto done;
127362306a36Sopenharmony_ci	} else if (strncmp(curr_pos, "trylock ", 8) == 0) {
127462306a36Sopenharmony_ci		curr_pos += 8;
127562306a36Sopenharmony_ci		remaining -= 8;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci		pr_debug("client 0x%p called 'trylock'\n", priv);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci		if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) {
128062306a36Sopenharmony_ci			ret_val = -EPROTO;
128162306a36Sopenharmony_ci			goto done;
128262306a36Sopenharmony_ci		}
128362306a36Sopenharmony_ci		/* TODO: Add this?
128462306a36Sopenharmony_ci		   if (io_state == VGA_RSRC_NONE) {
128562306a36Sopenharmony_ci		   ret_val = -EPROTO;
128662306a36Sopenharmony_ci		   goto done;
128762306a36Sopenharmony_ci		   }
128862306a36Sopenharmony_ci		 */
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		pdev = priv->target;
129162306a36Sopenharmony_ci		if (priv->target == NULL) {
129262306a36Sopenharmony_ci			ret_val = -ENODEV;
129362306a36Sopenharmony_ci			goto done;
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		if (vga_tryget(pdev, io_state)) {
129762306a36Sopenharmony_ci			/* Update the client's locks lists... */
129862306a36Sopenharmony_ci			for (i = 0; i < MAX_USER_CARDS; i++) {
129962306a36Sopenharmony_ci				if (priv->cards[i].pdev == pdev) {
130062306a36Sopenharmony_ci					if (io_state & VGA_RSRC_LEGACY_IO)
130162306a36Sopenharmony_ci						priv->cards[i].io_cnt++;
130262306a36Sopenharmony_ci					if (io_state & VGA_RSRC_LEGACY_MEM)
130362306a36Sopenharmony_ci						priv->cards[i].mem_cnt++;
130462306a36Sopenharmony_ci					break;
130562306a36Sopenharmony_ci				}
130662306a36Sopenharmony_ci			}
130762306a36Sopenharmony_ci			ret_val = count;
130862306a36Sopenharmony_ci			goto done;
130962306a36Sopenharmony_ci		} else {
131062306a36Sopenharmony_ci			ret_val = -EBUSY;
131162306a36Sopenharmony_ci			goto done;
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	} else if (strncmp(curr_pos, "target ", 7) == 0) {
131562306a36Sopenharmony_ci		unsigned int domain, bus, devfn;
131662306a36Sopenharmony_ci		struct vga_device *vgadev;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci		curr_pos += 7;
131962306a36Sopenharmony_ci		remaining -= 7;
132062306a36Sopenharmony_ci		pr_debug("client 0x%p called 'target'\n", priv);
132162306a36Sopenharmony_ci		/* If target is default */
132262306a36Sopenharmony_ci		if (!strncmp(curr_pos, "default", 7))
132362306a36Sopenharmony_ci			pdev = pci_dev_get(vga_default_device());
132462306a36Sopenharmony_ci		else {
132562306a36Sopenharmony_ci			if (!vga_pci_str_to_vars(curr_pos, remaining,
132662306a36Sopenharmony_ci						 &domain, &bus, &devfn)) {
132762306a36Sopenharmony_ci				ret_val = -EPROTO;
132862306a36Sopenharmony_ci				goto done;
132962306a36Sopenharmony_ci			}
133062306a36Sopenharmony_ci			pdev = pci_get_domain_bus_and_slot(domain, bus, devfn);
133162306a36Sopenharmony_ci			if (!pdev) {
133262306a36Sopenharmony_ci				pr_debug("invalid PCI address %04x:%02x:%02x.%x\n",
133362306a36Sopenharmony_ci					 domain, bus, PCI_SLOT(devfn),
133462306a36Sopenharmony_ci					 PCI_FUNC(devfn));
133562306a36Sopenharmony_ci				ret_val = -ENODEV;
133662306a36Sopenharmony_ci				goto done;
133762306a36Sopenharmony_ci			}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci			pr_debug("%s ==> %04x:%02x:%02x.%x pdev %p\n", curr_pos,
134062306a36Sopenharmony_ci				domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
134162306a36Sopenharmony_ci				pdev);
134262306a36Sopenharmony_ci		}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci		vgadev = vgadev_find(pdev);
134562306a36Sopenharmony_ci		pr_debug("vgadev %p\n", vgadev);
134662306a36Sopenharmony_ci		if (vgadev == NULL) {
134762306a36Sopenharmony_ci			if (pdev) {
134862306a36Sopenharmony_ci				vgaarb_dbg(&pdev->dev, "not a VGA device\n");
134962306a36Sopenharmony_ci				pci_dev_put(pdev);
135062306a36Sopenharmony_ci			}
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci			ret_val = -ENODEV;
135362306a36Sopenharmony_ci			goto done;
135462306a36Sopenharmony_ci		}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci		priv->target = pdev;
135762306a36Sopenharmony_ci		for (i = 0; i < MAX_USER_CARDS; i++) {
135862306a36Sopenharmony_ci			if (priv->cards[i].pdev == pdev)
135962306a36Sopenharmony_ci				break;
136062306a36Sopenharmony_ci			if (priv->cards[i].pdev == NULL) {
136162306a36Sopenharmony_ci				priv->cards[i].pdev = pdev;
136262306a36Sopenharmony_ci				priv->cards[i].io_cnt = 0;
136362306a36Sopenharmony_ci				priv->cards[i].mem_cnt = 0;
136462306a36Sopenharmony_ci				break;
136562306a36Sopenharmony_ci			}
136662306a36Sopenharmony_ci		}
136762306a36Sopenharmony_ci		if (i == MAX_USER_CARDS) {
136862306a36Sopenharmony_ci			vgaarb_dbg(&pdev->dev, "maximum user cards (%d) number reached, ignoring this one!\n",
136962306a36Sopenharmony_ci				MAX_USER_CARDS);
137062306a36Sopenharmony_ci			pci_dev_put(pdev);
137162306a36Sopenharmony_ci			/* XXX: Which value to return? */
137262306a36Sopenharmony_ci			ret_val =  -ENOMEM;
137362306a36Sopenharmony_ci			goto done;
137462306a36Sopenharmony_ci		}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		ret_val = count;
137762306a36Sopenharmony_ci		pci_dev_put(pdev);
137862306a36Sopenharmony_ci		goto done;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	} else if (strncmp(curr_pos, "decodes ", 8) == 0) {
138262306a36Sopenharmony_ci		curr_pos += 8;
138362306a36Sopenharmony_ci		remaining -= 8;
138462306a36Sopenharmony_ci		pr_debug("client 0x%p called 'decodes'\n", priv);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci		if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) {
138762306a36Sopenharmony_ci			ret_val = -EPROTO;
138862306a36Sopenharmony_ci			goto done;
138962306a36Sopenharmony_ci		}
139062306a36Sopenharmony_ci		pdev = priv->target;
139162306a36Sopenharmony_ci		if (priv->target == NULL) {
139262306a36Sopenharmony_ci			ret_val = -ENODEV;
139362306a36Sopenharmony_ci			goto done;
139462306a36Sopenharmony_ci		}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci		__vga_set_legacy_decoding(pdev, io_state, true);
139762306a36Sopenharmony_ci		ret_val = count;
139862306a36Sopenharmony_ci		goto done;
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci	/* If we got here, the message written is not part of the protocol! */
140162306a36Sopenharmony_ci	return -EPROTO;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_cidone:
140462306a36Sopenharmony_ci	return ret_val;
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistatic __poll_t vga_arb_fpoll(struct file *file, poll_table *wait)
140862306a36Sopenharmony_ci{
140962306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	poll_wait(file, &vga_wait_queue, wait);
141262306a36Sopenharmony_ci	return EPOLLIN;
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_cistatic int vga_arb_open(struct inode *inode, struct file *file)
141662306a36Sopenharmony_ci{
141762306a36Sopenharmony_ci	struct vga_arb_private *priv;
141862306a36Sopenharmony_ci	unsigned long flags;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
142362306a36Sopenharmony_ci	if (priv == NULL)
142462306a36Sopenharmony_ci		return -ENOMEM;
142562306a36Sopenharmony_ci	spin_lock_init(&priv->lock);
142662306a36Sopenharmony_ci	file->private_data = priv;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	spin_lock_irqsave(&vga_user_lock, flags);
142962306a36Sopenharmony_ci	list_add(&priv->list, &vga_user_list);
143062306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_user_lock, flags);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	/* Set the client's lists of locks */
143362306a36Sopenharmony_ci	priv->target = vga_default_device(); /* Maybe this is still null! */
143462306a36Sopenharmony_ci	priv->cards[0].pdev = priv->target;
143562306a36Sopenharmony_ci	priv->cards[0].io_cnt = 0;
143662306a36Sopenharmony_ci	priv->cards[0].mem_cnt = 0;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	return 0;
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_cistatic int vga_arb_release(struct inode *inode, struct file *file)
144262306a36Sopenharmony_ci{
144362306a36Sopenharmony_ci	struct vga_arb_private *priv = file->private_data;
144462306a36Sopenharmony_ci	struct vga_arb_user_card *uc;
144562306a36Sopenharmony_ci	unsigned long flags;
144662306a36Sopenharmony_ci	int i;
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	spin_lock_irqsave(&vga_user_lock, flags);
145162306a36Sopenharmony_ci	list_del(&priv->list);
145262306a36Sopenharmony_ci	for (i = 0; i < MAX_USER_CARDS; i++) {
145362306a36Sopenharmony_ci		uc = &priv->cards[i];
145462306a36Sopenharmony_ci		if (uc->pdev == NULL)
145562306a36Sopenharmony_ci			continue;
145662306a36Sopenharmony_ci		vgaarb_dbg(&uc->pdev->dev, "uc->io_cnt == %d, uc->mem_cnt == %d\n",
145762306a36Sopenharmony_ci			uc->io_cnt, uc->mem_cnt);
145862306a36Sopenharmony_ci		while (uc->io_cnt--)
145962306a36Sopenharmony_ci			vga_put(uc->pdev, VGA_RSRC_LEGACY_IO);
146062306a36Sopenharmony_ci		while (uc->mem_cnt--)
146162306a36Sopenharmony_ci			vga_put(uc->pdev, VGA_RSRC_LEGACY_MEM);
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_user_lock, flags);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	kfree(priv);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return 0;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci/*
147162306a36Sopenharmony_ci * Callback any registered clients to let them know we have a change in VGA
147262306a36Sopenharmony_ci * cards.
147362306a36Sopenharmony_ci */
147462306a36Sopenharmony_cistatic void vga_arbiter_notify_clients(void)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	struct vga_device *vgadev;
147762306a36Sopenharmony_ci	unsigned long flags;
147862306a36Sopenharmony_ci	unsigned int new_decodes;
147962306a36Sopenharmony_ci	bool new_state;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (!vga_arbiter_used)
148262306a36Sopenharmony_ci		return;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	new_state = (vga_count > 1) ? false : true;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
148762306a36Sopenharmony_ci	list_for_each_entry(vgadev, &vga_list, list) {
148862306a36Sopenharmony_ci		if (vgadev->set_decode) {
148962306a36Sopenharmony_ci			new_decodes = vgadev->set_decode(vgadev->pdev,
149062306a36Sopenharmony_ci							 new_state);
149162306a36Sopenharmony_ci			vga_update_device_decodes(vgadev, new_decodes);
149262306a36Sopenharmony_ci		}
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_cistatic int pci_notify(struct notifier_block *nb, unsigned long action,
149862306a36Sopenharmony_ci		      void *data)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	struct device *dev = data;
150162306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
150262306a36Sopenharmony_ci	bool notify = false;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	vgaarb_dbg(dev, "%s\n", __func__);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	/*
150762306a36Sopenharmony_ci	 * For now, we're only interested in devices added and removed.
150862306a36Sopenharmony_ci	 * I didn't test this thing here, so someone needs to double check
150962306a36Sopenharmony_ci	 * for the cases of hot-pluggable VGA cards.
151062306a36Sopenharmony_ci	 */
151162306a36Sopenharmony_ci	if (action == BUS_NOTIFY_ADD_DEVICE)
151262306a36Sopenharmony_ci		notify = vga_arbiter_add_pci_device(pdev);
151362306a36Sopenharmony_ci	else if (action == BUS_NOTIFY_DEL_DEVICE)
151462306a36Sopenharmony_ci		notify = vga_arbiter_del_pci_device(pdev);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (notify)
151762306a36Sopenharmony_ci		vga_arbiter_notify_clients();
151862306a36Sopenharmony_ci	return 0;
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_cistatic struct notifier_block pci_notifier = {
152262306a36Sopenharmony_ci	.notifier_call = pci_notify,
152362306a36Sopenharmony_ci};
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic const struct file_operations vga_arb_device_fops = {
152662306a36Sopenharmony_ci	.read = vga_arb_read,
152762306a36Sopenharmony_ci	.write = vga_arb_write,
152862306a36Sopenharmony_ci	.poll = vga_arb_fpoll,
152962306a36Sopenharmony_ci	.open = vga_arb_open,
153062306a36Sopenharmony_ci	.release = vga_arb_release,
153162306a36Sopenharmony_ci	.llseek = noop_llseek,
153262306a36Sopenharmony_ci};
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_cistatic struct miscdevice vga_arb_device = {
153562306a36Sopenharmony_ci	MISC_DYNAMIC_MINOR, "vga_arbiter", &vga_arb_device_fops
153662306a36Sopenharmony_ci};
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_cistatic int __init vga_arb_device_init(void)
153962306a36Sopenharmony_ci{
154062306a36Sopenharmony_ci	int rc;
154162306a36Sopenharmony_ci	struct pci_dev *pdev;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	rc = misc_register(&vga_arb_device);
154462306a36Sopenharmony_ci	if (rc < 0)
154562306a36Sopenharmony_ci		pr_err("error %d registering device\n", rc);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	bus_register_notifier(&pci_bus_type, &pci_notifier);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/* Add all VGA class PCI devices by default */
155062306a36Sopenharmony_ci	pdev = NULL;
155162306a36Sopenharmony_ci	while ((pdev =
155262306a36Sopenharmony_ci		pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
155362306a36Sopenharmony_ci			       PCI_ANY_ID, pdev)) != NULL)
155462306a36Sopenharmony_ci		vga_arbiter_add_pci_device(pdev);
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	pr_info("loaded\n");
155762306a36Sopenharmony_ci	return rc;
155862306a36Sopenharmony_ci}
155962306a36Sopenharmony_cisubsys_initcall_sync(vga_arb_device_init);
1560