18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * vgaarb.c: Implements the VGA arbitration. For details refer to
38c2ecf20Sopenharmony_ci * Documentation/gpu/vgaarbiter.rst
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * (C) Copyright 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
78c2ecf20Sopenharmony_ci * (C) Copyright 2007 Paulo R. Zanoni <przanoni@gmail.com>
88c2ecf20Sopenharmony_ci * (C) Copyright 2007, 2009 Tiago Vignatti <vignatti@freedesktop.org>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
118c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
128c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
138c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
148c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
158c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
188c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
198c2ecf20Sopenharmony_ci * Software.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
228c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
238c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
248c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
258c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
268c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
278c2ecf20Sopenharmony_ci * DEALINGS
288c2ecf20Sopenharmony_ci * IN THE SOFTWARE.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "vgaarb: " fmt
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define vgaarb_dbg(dev, fmt, arg...)	dev_dbg(dev, "vgaarb: " fmt, ##arg)
358c2ecf20Sopenharmony_ci#define vgaarb_info(dev, fmt, arg...)	dev_info(dev, "vgaarb: " fmt, ##arg)
368c2ecf20Sopenharmony_ci#define vgaarb_err(dev, fmt, arg...)	dev_err(dev, "vgaarb: " fmt, ##arg)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include <linux/module.h>
398c2ecf20Sopenharmony_ci#include <linux/kernel.h>
408c2ecf20Sopenharmony_ci#include <linux/pci.h>
418c2ecf20Sopenharmony_ci#include <linux/errno.h>
428c2ecf20Sopenharmony_ci#include <linux/init.h>
438c2ecf20Sopenharmony_ci#include <linux/list.h>
448c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
458c2ecf20Sopenharmony_ci#include <linux/wait.h>
468c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
478c2ecf20Sopenharmony_ci#include <linux/poll.h>
488c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
498c2ecf20Sopenharmony_ci#include <linux/slab.h>
508c2ecf20Sopenharmony_ci#include <linux/screen_info.h>
518c2ecf20Sopenharmony_ci#include <linux/vt.h>
528c2ecf20Sopenharmony_ci#include <linux/console.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#include <linux/vgaarb.h>
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void vga_arbiter_notify_clients(void);
598c2ecf20Sopenharmony_ci/*
608c2ecf20Sopenharmony_ci * We keep a list of all vga devices in the system to speed
618c2ecf20Sopenharmony_ci * up the various operations of the arbiter
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_cistruct vga_device {
648c2ecf20Sopenharmony_ci	struct list_head list;
658c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
668c2ecf20Sopenharmony_ci	unsigned int decodes;	/* what does it decodes */
678c2ecf20Sopenharmony_ci	unsigned int owns;	/* what does it owns */
688c2ecf20Sopenharmony_ci	unsigned int locks;	/* what does it locks */
698c2ecf20Sopenharmony_ci	unsigned int io_lock_cnt;	/* legacy IO lock count */
708c2ecf20Sopenharmony_ci	unsigned int mem_lock_cnt;	/* legacy MEM lock count */
718c2ecf20Sopenharmony_ci	unsigned int io_norm_cnt;	/* normal IO count */
728c2ecf20Sopenharmony_ci	unsigned int mem_norm_cnt;	/* normal MEM count */
738c2ecf20Sopenharmony_ci	bool bridge_has_one_vga;
748c2ecf20Sopenharmony_ci	/* allow IRQ enable/disable hook */
758c2ecf20Sopenharmony_ci	void *cookie;
768c2ecf20Sopenharmony_ci	void (*irq_set_state)(void *cookie, bool enable);
778c2ecf20Sopenharmony_ci	unsigned int (*set_vga_decode)(void *cookie, bool decode);
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic LIST_HEAD(vga_list);
818c2ecf20Sopenharmony_cistatic int vga_count, vga_decode_count;
828c2ecf20Sopenharmony_cistatic bool vga_arbiter_used;
838c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(vga_lock);
848c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(vga_wait_queue);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic const char *vga_iostate_to_str(unsigned int iostate)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	/* Ignore VGA_RSRC_IO and VGA_RSRC_MEM */
908c2ecf20Sopenharmony_ci	iostate &= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
918c2ecf20Sopenharmony_ci	switch (iostate) {
928c2ecf20Sopenharmony_ci	case VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM:
938c2ecf20Sopenharmony_ci		return "io+mem";
948c2ecf20Sopenharmony_ci	case VGA_RSRC_LEGACY_IO:
958c2ecf20Sopenharmony_ci		return "io";
968c2ecf20Sopenharmony_ci	case VGA_RSRC_LEGACY_MEM:
978c2ecf20Sopenharmony_ci		return "mem";
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	return "none";
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int vga_str_to_iostate(char *buf, int str_size, int *io_state)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	/* we could in theory hand out locks on IO and mem
1058c2ecf20Sopenharmony_ci	 * separately to userspace but it can cause deadlocks */
1068c2ecf20Sopenharmony_ci	if (strncmp(buf, "none", 4) == 0) {
1078c2ecf20Sopenharmony_ci		*io_state = VGA_RSRC_NONE;
1088c2ecf20Sopenharmony_ci		return 1;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* XXX We're not chekcing the str_size! */
1128c2ecf20Sopenharmony_ci	if (strncmp(buf, "io+mem", 6) == 0)
1138c2ecf20Sopenharmony_ci		goto both;
1148c2ecf20Sopenharmony_ci	else if (strncmp(buf, "io", 2) == 0)
1158c2ecf20Sopenharmony_ci		goto both;
1168c2ecf20Sopenharmony_ci	else if (strncmp(buf, "mem", 3) == 0)
1178c2ecf20Sopenharmony_ci		goto both;
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ciboth:
1208c2ecf20Sopenharmony_ci	*io_state = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
1218c2ecf20Sopenharmony_ci	return 1;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/* this is only used a cookie - it should not be dereferenced */
1258c2ecf20Sopenharmony_cistatic struct pci_dev *vga_default;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic void vga_arb_device_card_gone(struct pci_dev *pdev);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* Find somebody in our list */
1308c2ecf20Sopenharmony_cistatic struct vga_device *vgadev_find(struct pci_dev *pdev)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	list_for_each_entry(vgadev, &vga_list, list)
1358c2ecf20Sopenharmony_ci		if (pdev == vgadev->pdev)
1368c2ecf20Sopenharmony_ci			return vgadev;
1378c2ecf20Sopenharmony_ci	return NULL;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/**
1418c2ecf20Sopenharmony_ci * vga_default_device - return the default VGA device, for vgacon
1428c2ecf20Sopenharmony_ci *
1438c2ecf20Sopenharmony_ci * This can be defined by the platform. The default implementation
1448c2ecf20Sopenharmony_ci * is rather dumb and will probably only work properly on single
1458c2ecf20Sopenharmony_ci * vga card setups and/or x86 platforms.
1468c2ecf20Sopenharmony_ci *
1478c2ecf20Sopenharmony_ci * If your VGA default device is not PCI, you'll have to return
1488c2ecf20Sopenharmony_ci * NULL here. In this case, I assume it will not conflict with
1498c2ecf20Sopenharmony_ci * any PCI card. If this is not true, I'll have to define two archs
1508c2ecf20Sopenharmony_ci * hooks for enabling/disabling the VGA default device if that is
1518c2ecf20Sopenharmony_ci * possible. This may be a problem with real _ISA_ VGA cards, in
1528c2ecf20Sopenharmony_ci * addition to a PCI one. I don't know at this point how to deal
1538c2ecf20Sopenharmony_ci * with that card. Can theirs IOs be disabled at all ? If not, then
1548c2ecf20Sopenharmony_ci * I suppose it's a matter of having the proper arch hook telling
1558c2ecf20Sopenharmony_ci * us about it, so we basically never allow anybody to succeed a
1568c2ecf20Sopenharmony_ci * vga_get()...
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_cistruct pci_dev *vga_default_device(void)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return vga_default;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vga_default_device);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_civoid vga_set_default_device(struct pci_dev *pdev)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	if (vga_default == pdev)
1678c2ecf20Sopenharmony_ci		return;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	pci_dev_put(vga_default);
1708c2ecf20Sopenharmony_ci	vga_default = pci_dev_get(pdev);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/**
1748c2ecf20Sopenharmony_ci * vga_remove_vgacon - deactivete vga console
1758c2ecf20Sopenharmony_ci *
1768c2ecf20Sopenharmony_ci * Unbind and unregister vgacon in case pdev is the default vga
1778c2ecf20Sopenharmony_ci * device.  Can be called by gpu drivers on initialization to make
1788c2ecf20Sopenharmony_ci * sure vga register access done by vgacon will not disturb the
1798c2ecf20Sopenharmony_ci * device.
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * @pdev: pci device.
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_ci#if !defined(CONFIG_VGA_CONSOLE)
1848c2ecf20Sopenharmony_ciint vga_remove_vgacon(struct pci_dev *pdev)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci#elif !defined(CONFIG_DUMMY_CONSOLE)
1898c2ecf20Sopenharmony_ciint vga_remove_vgacon(struct pci_dev *pdev)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	return -ENODEV;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci#else
1948c2ecf20Sopenharmony_ciint vga_remove_vgacon(struct pci_dev *pdev)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int ret = 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (pdev != vga_default)
1998c2ecf20Sopenharmony_ci		return 0;
2008c2ecf20Sopenharmony_ci	vgaarb_info(&pdev->dev, "deactivate vga console\n");
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	console_lock();
2038c2ecf20Sopenharmony_ci	if (con_is_bound(&vga_con))
2048c2ecf20Sopenharmony_ci		ret = do_take_over_console(&dummy_con, 0,
2058c2ecf20Sopenharmony_ci					   MAX_NR_CONSOLES - 1, 1);
2068c2ecf20Sopenharmony_ci	if (ret == 0) {
2078c2ecf20Sopenharmony_ci		ret = do_unregister_con_driver(&vga_con);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		/* Ignore "already unregistered". */
2108c2ecf20Sopenharmony_ci		if (ret == -ENODEV)
2118c2ecf20Sopenharmony_ci			ret = 0;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	console_unlock();
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return ret;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci#endif
2188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vga_remove_vgacon);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic inline void vga_irq_set_state(struct vga_device *vgadev, bool state)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	if (vgadev->irq_set_state)
2238c2ecf20Sopenharmony_ci		vgadev->irq_set_state(vgadev->cookie, state);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/* If we don't ever use VGA arb we should avoid
2288c2ecf20Sopenharmony_ci   turning off anything anywhere due to old X servers getting
2298c2ecf20Sopenharmony_ci   confused about the boot device not being VGA */
2308c2ecf20Sopenharmony_cistatic void vga_check_first_use(void)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	/* we should inform all GPUs in the system that
2338c2ecf20Sopenharmony_ci	 * VGA arb has occurred and to try and disable resources
2348c2ecf20Sopenharmony_ci	 * if they can */
2358c2ecf20Sopenharmony_ci	if (!vga_arbiter_used) {
2368c2ecf20Sopenharmony_ci		vga_arbiter_used = true;
2378c2ecf20Sopenharmony_ci		vga_arbiter_notify_clients();
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic struct vga_device *__vga_tryget(struct vga_device *vgadev,
2428c2ecf20Sopenharmony_ci				       unsigned int rsrc)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct device *dev = &vgadev->pdev->dev;
2458c2ecf20Sopenharmony_ci	unsigned int wants, legacy_wants, match;
2468c2ecf20Sopenharmony_ci	struct vga_device *conflict;
2478c2ecf20Sopenharmony_ci	unsigned int pci_bits;
2488c2ecf20Sopenharmony_ci	u32 flags = 0;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Account for "normal" resources to lock. If we decode the legacy,
2518c2ecf20Sopenharmony_ci	 * counterpart, we need to request it as well
2528c2ecf20Sopenharmony_ci	 */
2538c2ecf20Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_IO) &&
2548c2ecf20Sopenharmony_ci	    (vgadev->decodes & VGA_RSRC_LEGACY_IO))
2558c2ecf20Sopenharmony_ci		rsrc |= VGA_RSRC_LEGACY_IO;
2568c2ecf20Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_MEM) &&
2578c2ecf20Sopenharmony_ci	    (vgadev->decodes & VGA_RSRC_LEGACY_MEM))
2588c2ecf20Sopenharmony_ci		rsrc |= VGA_RSRC_LEGACY_MEM;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	vgaarb_dbg(dev, "%s: %d\n", __func__, rsrc);
2618c2ecf20Sopenharmony_ci	vgaarb_dbg(dev, "%s: owns: %d\n", __func__, vgadev->owns);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* Check what resources we need to acquire */
2648c2ecf20Sopenharmony_ci	wants = rsrc & ~vgadev->owns;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* We already own everything, just mark locked & bye bye */
2678c2ecf20Sopenharmony_ci	if (wants == 0)
2688c2ecf20Sopenharmony_ci		goto lock_them;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* We don't need to request a legacy resource, we just enable
2718c2ecf20Sopenharmony_ci	 * appropriate decoding and go
2728c2ecf20Sopenharmony_ci	 */
2738c2ecf20Sopenharmony_ci	legacy_wants = wants & VGA_RSRC_LEGACY_MASK;
2748c2ecf20Sopenharmony_ci	if (legacy_wants == 0)
2758c2ecf20Sopenharmony_ci		goto enable_them;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* Ok, we don't, let's find out how we need to kick off */
2788c2ecf20Sopenharmony_ci	list_for_each_entry(conflict, &vga_list, list) {
2798c2ecf20Sopenharmony_ci		unsigned int lwants = legacy_wants;
2808c2ecf20Sopenharmony_ci		unsigned int change_bridge = 0;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci		/* Don't conflict with myself */
2838c2ecf20Sopenharmony_ci		if (vgadev == conflict)
2848c2ecf20Sopenharmony_ci			continue;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		/* Check if the architecture allows a conflict between those
2878c2ecf20Sopenharmony_ci		 * 2 devices or if they are on separate domains
2888c2ecf20Sopenharmony_ci		 */
2898c2ecf20Sopenharmony_ci		if (!vga_conflicts(vgadev->pdev, conflict->pdev))
2908c2ecf20Sopenharmony_ci			continue;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		/* We have a possible conflict. before we go further, we must
2938c2ecf20Sopenharmony_ci		 * check if we sit on the same bus as the conflicting device.
2948c2ecf20Sopenharmony_ci		 * if we don't, then we must tie both IO and MEM resources
2958c2ecf20Sopenharmony_ci		 * together since there is only a single bit controlling
2968c2ecf20Sopenharmony_ci		 * VGA forwarding on P2P bridges
2978c2ecf20Sopenharmony_ci		 */
2988c2ecf20Sopenharmony_ci		if (vgadev->pdev->bus != conflict->pdev->bus) {
2998c2ecf20Sopenharmony_ci			change_bridge = 1;
3008c2ecf20Sopenharmony_ci			lwants = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		/* Check if the guy has a lock on the resource. If he does,
3048c2ecf20Sopenharmony_ci		 * return the conflicting entry
3058c2ecf20Sopenharmony_ci		 */
3068c2ecf20Sopenharmony_ci		if (conflict->locks & lwants)
3078c2ecf20Sopenharmony_ci			return conflict;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		/* Ok, now check if it owns the resource we want.  We can
3108c2ecf20Sopenharmony_ci		 * lock resources that are not decoded, therefore a device
3118c2ecf20Sopenharmony_ci		 * can own resources it doesn't decode.
3128c2ecf20Sopenharmony_ci		 */
3138c2ecf20Sopenharmony_ci		match = lwants & conflict->owns;
3148c2ecf20Sopenharmony_ci		if (!match)
3158c2ecf20Sopenharmony_ci			continue;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		/* looks like he doesn't have a lock, we can steal
3188c2ecf20Sopenharmony_ci		 * them from him
3198c2ecf20Sopenharmony_ci		 */
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		flags = 0;
3228c2ecf20Sopenharmony_ci		pci_bits = 0;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		/* If we can't control legacy resources via the bridge, we
3258c2ecf20Sopenharmony_ci		 * also need to disable normal decoding.
3268c2ecf20Sopenharmony_ci		 */
3278c2ecf20Sopenharmony_ci		if (!conflict->bridge_has_one_vga) {
3288c2ecf20Sopenharmony_ci			if ((match & conflict->decodes) & VGA_RSRC_LEGACY_MEM)
3298c2ecf20Sopenharmony_ci				pci_bits |= PCI_COMMAND_MEMORY;
3308c2ecf20Sopenharmony_ci			if ((match & conflict->decodes) & VGA_RSRC_LEGACY_IO)
3318c2ecf20Sopenharmony_ci				pci_bits |= PCI_COMMAND_IO;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci			if (pci_bits) {
3348c2ecf20Sopenharmony_ci				vga_irq_set_state(conflict, false);
3358c2ecf20Sopenharmony_ci				flags |= PCI_VGA_STATE_CHANGE_DECODES;
3368c2ecf20Sopenharmony_ci			}
3378c2ecf20Sopenharmony_ci		}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		if (change_bridge)
3408c2ecf20Sopenharmony_ci			flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		pci_set_vga_state(conflict->pdev, false, pci_bits, flags);
3438c2ecf20Sopenharmony_ci		conflict->owns &= ~match;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		/* If we disabled normal decoding, reflect it in owns */
3468c2ecf20Sopenharmony_ci		if (pci_bits & PCI_COMMAND_MEMORY)
3478c2ecf20Sopenharmony_ci			conflict->owns &= ~VGA_RSRC_NORMAL_MEM;
3488c2ecf20Sopenharmony_ci		if (pci_bits & PCI_COMMAND_IO)
3498c2ecf20Sopenharmony_ci			conflict->owns &= ~VGA_RSRC_NORMAL_IO;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cienable_them:
3538c2ecf20Sopenharmony_ci	/* ok dude, we got it, everybody conflicting has been disabled, let's
3548c2ecf20Sopenharmony_ci	 * enable us.  Mark any bits in "owns" regardless of whether we
3558c2ecf20Sopenharmony_ci	 * decoded them.  We can lock resources we don't decode, therefore
3568c2ecf20Sopenharmony_ci	 * we must track them via "owns".
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	flags = 0;
3598c2ecf20Sopenharmony_ci	pci_bits = 0;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (!vgadev->bridge_has_one_vga) {
3628c2ecf20Sopenharmony_ci		flags |= PCI_VGA_STATE_CHANGE_DECODES;
3638c2ecf20Sopenharmony_ci		if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
3648c2ecf20Sopenharmony_ci			pci_bits |= PCI_COMMAND_MEMORY;
3658c2ecf20Sopenharmony_ci		if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
3668c2ecf20Sopenharmony_ci			pci_bits |= PCI_COMMAND_IO;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci	if (wants & VGA_RSRC_LEGACY_MASK)
3698c2ecf20Sopenharmony_ci		flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	pci_set_vga_state(vgadev->pdev, true, pci_bits, flags);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (!vgadev->bridge_has_one_vga)
3748c2ecf20Sopenharmony_ci		vga_irq_set_state(vgadev, true);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	vgadev->owns |= wants;
3778c2ecf20Sopenharmony_cilock_them:
3788c2ecf20Sopenharmony_ci	vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK);
3798c2ecf20Sopenharmony_ci	if (rsrc & VGA_RSRC_LEGACY_IO)
3808c2ecf20Sopenharmony_ci		vgadev->io_lock_cnt++;
3818c2ecf20Sopenharmony_ci	if (rsrc & VGA_RSRC_LEGACY_MEM)
3828c2ecf20Sopenharmony_ci		vgadev->mem_lock_cnt++;
3838c2ecf20Sopenharmony_ci	if (rsrc & VGA_RSRC_NORMAL_IO)
3848c2ecf20Sopenharmony_ci		vgadev->io_norm_cnt++;
3858c2ecf20Sopenharmony_ci	if (rsrc & VGA_RSRC_NORMAL_MEM)
3868c2ecf20Sopenharmony_ci		vgadev->mem_norm_cnt++;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return NULL;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void __vga_put(struct vga_device *vgadev, unsigned int rsrc)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct device *dev = &vgadev->pdev->dev;
3948c2ecf20Sopenharmony_ci	unsigned int old_locks = vgadev->locks;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	vgaarb_dbg(dev, "%s\n", __func__);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* Update our counters, and account for equivalent legacy resources
3998c2ecf20Sopenharmony_ci	 * if we decode them
4008c2ecf20Sopenharmony_ci	 */
4018c2ecf20Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_IO) && vgadev->io_norm_cnt > 0) {
4028c2ecf20Sopenharmony_ci		vgadev->io_norm_cnt--;
4038c2ecf20Sopenharmony_ci		if (vgadev->decodes & VGA_RSRC_LEGACY_IO)
4048c2ecf20Sopenharmony_ci			rsrc |= VGA_RSRC_LEGACY_IO;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci	if ((rsrc & VGA_RSRC_NORMAL_MEM) && vgadev->mem_norm_cnt > 0) {
4078c2ecf20Sopenharmony_ci		vgadev->mem_norm_cnt--;
4088c2ecf20Sopenharmony_ci		if (vgadev->decodes & VGA_RSRC_LEGACY_MEM)
4098c2ecf20Sopenharmony_ci			rsrc |= VGA_RSRC_LEGACY_MEM;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	if ((rsrc & VGA_RSRC_LEGACY_IO) && vgadev->io_lock_cnt > 0)
4128c2ecf20Sopenharmony_ci		vgadev->io_lock_cnt--;
4138c2ecf20Sopenharmony_ci	if ((rsrc & VGA_RSRC_LEGACY_MEM) && vgadev->mem_lock_cnt > 0)
4148c2ecf20Sopenharmony_ci		vgadev->mem_lock_cnt--;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* Just clear lock bits, we do lazy operations so we don't really
4178c2ecf20Sopenharmony_ci	 * have to bother about anything else at this point
4188c2ecf20Sopenharmony_ci	 */
4198c2ecf20Sopenharmony_ci	if (vgadev->io_lock_cnt == 0)
4208c2ecf20Sopenharmony_ci		vgadev->locks &= ~VGA_RSRC_LEGACY_IO;
4218c2ecf20Sopenharmony_ci	if (vgadev->mem_lock_cnt == 0)
4228c2ecf20Sopenharmony_ci		vgadev->locks &= ~VGA_RSRC_LEGACY_MEM;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* Kick the wait queue in case somebody was waiting if we actually
4258c2ecf20Sopenharmony_ci	 * released something
4268c2ecf20Sopenharmony_ci	 */
4278c2ecf20Sopenharmony_ci	if (old_locks != vgadev->locks)
4288c2ecf20Sopenharmony_ci		wake_up_all(&vga_wait_queue);
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/**
4328c2ecf20Sopenharmony_ci * vga_get - acquire & locks VGA resources
4338c2ecf20Sopenharmony_ci * @pdev: pci device of the VGA card or NULL for the system default
4348c2ecf20Sopenharmony_ci * @rsrc: bit mask of resources to acquire and lock
4358c2ecf20Sopenharmony_ci * @interruptible: blocking should be interruptible by signals ?
4368c2ecf20Sopenharmony_ci *
4378c2ecf20Sopenharmony_ci * This function acquires VGA resources for the given card and mark those
4388c2ecf20Sopenharmony_ci * resources locked. If the resource requested are "normal" (and not legacy)
4398c2ecf20Sopenharmony_ci * resources, the arbiter will first check whether the card is doing legacy
4408c2ecf20Sopenharmony_ci * decoding for that type of resource. If yes, the lock is "converted" into a
4418c2ecf20Sopenharmony_ci * legacy resource lock.
4428c2ecf20Sopenharmony_ci *
4438c2ecf20Sopenharmony_ci * The arbiter will first look for all VGA cards that might conflict and disable
4448c2ecf20Sopenharmony_ci * their IOs and/or Memory access, including VGA forwarding on P2P bridges if
4458c2ecf20Sopenharmony_ci * necessary, so that the requested resources can be used. Then, the card is
4468c2ecf20Sopenharmony_ci * marked as locking these resources and the IO and/or Memory accesses are
4478c2ecf20Sopenharmony_ci * enabled on the card (including VGA forwarding on parent P2P bridges if any).
4488c2ecf20Sopenharmony_ci *
4498c2ecf20Sopenharmony_ci * This function will block if some conflicting card is already locking one of
4508c2ecf20Sopenharmony_ci * the required resources (or any resource on a different bus segment, since P2P
4518c2ecf20Sopenharmony_ci * bridges don't differentiate VGA memory and IO afaik). You can indicate
4528c2ecf20Sopenharmony_ci * whether this blocking should be interruptible by a signal (for userland
4538c2ecf20Sopenharmony_ci * interface) or not.
4548c2ecf20Sopenharmony_ci *
4558c2ecf20Sopenharmony_ci * Must not be called at interrupt time or in atomic context.  If the card
4568c2ecf20Sopenharmony_ci * already owns the resources, the function succeeds.  Nested calls are
4578c2ecf20Sopenharmony_ci * supported (a per-resource counter is maintained)
4588c2ecf20Sopenharmony_ci *
4598c2ecf20Sopenharmony_ci * On success, release the VGA resource again with vga_put().
4608c2ecf20Sopenharmony_ci *
4618c2ecf20Sopenharmony_ci * Returns:
4628c2ecf20Sopenharmony_ci *
4638c2ecf20Sopenharmony_ci * 0 on success, negative error code on failure.
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_ciint vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct vga_device *vgadev, *conflict;
4688c2ecf20Sopenharmony_ci	unsigned long flags;
4698c2ecf20Sopenharmony_ci	wait_queue_entry_t wait;
4708c2ecf20Sopenharmony_ci	int rc = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	vga_check_first_use();
4738c2ecf20Sopenharmony_ci	/* The one who calls us should check for this, but lets be sure... */
4748c2ecf20Sopenharmony_ci	if (pdev == NULL)
4758c2ecf20Sopenharmony_ci		pdev = vga_default_device();
4768c2ecf20Sopenharmony_ci	if (pdev == NULL)
4778c2ecf20Sopenharmony_ci		return 0;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	for (;;) {
4808c2ecf20Sopenharmony_ci		spin_lock_irqsave(&vga_lock, flags);
4818c2ecf20Sopenharmony_ci		vgadev = vgadev_find(pdev);
4828c2ecf20Sopenharmony_ci		if (vgadev == NULL) {
4838c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&vga_lock, flags);
4848c2ecf20Sopenharmony_ci			rc = -ENODEV;
4858c2ecf20Sopenharmony_ci			break;
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci		conflict = __vga_tryget(vgadev, rsrc);
4888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vga_lock, flags);
4898c2ecf20Sopenharmony_ci		if (conflict == NULL)
4908c2ecf20Sopenharmony_ci			break;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		/* We have a conflict, we wait until somebody kicks the
4948c2ecf20Sopenharmony_ci		 * work queue. Currently we have one work queue that we
4958c2ecf20Sopenharmony_ci		 * kick each time some resources are released, but it would
4968c2ecf20Sopenharmony_ci		 * be fairly easy to have a per device one so that we only
4978c2ecf20Sopenharmony_ci		 * need to attach to the conflicting device
4988c2ecf20Sopenharmony_ci		 */
4998c2ecf20Sopenharmony_ci		init_waitqueue_entry(&wait, current);
5008c2ecf20Sopenharmony_ci		add_wait_queue(&vga_wait_queue, &wait);
5018c2ecf20Sopenharmony_ci		set_current_state(interruptible ?
5028c2ecf20Sopenharmony_ci				  TASK_INTERRUPTIBLE :
5038c2ecf20Sopenharmony_ci				  TASK_UNINTERRUPTIBLE);
5048c2ecf20Sopenharmony_ci		if (interruptible && signal_pending(current)) {
5058c2ecf20Sopenharmony_ci			__set_current_state(TASK_RUNNING);
5068c2ecf20Sopenharmony_ci			remove_wait_queue(&vga_wait_queue, &wait);
5078c2ecf20Sopenharmony_ci			rc = -ERESTARTSYS;
5088c2ecf20Sopenharmony_ci			break;
5098c2ecf20Sopenharmony_ci		}
5108c2ecf20Sopenharmony_ci		schedule();
5118c2ecf20Sopenharmony_ci		remove_wait_queue(&vga_wait_queue, &wait);
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci	return rc;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vga_get);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci/**
5188c2ecf20Sopenharmony_ci * vga_tryget - try to acquire & lock legacy VGA resources
5198c2ecf20Sopenharmony_ci * @pdev: pci devivce of VGA card or NULL for system default
5208c2ecf20Sopenharmony_ci * @rsrc: bit mask of resources to acquire and lock
5218c2ecf20Sopenharmony_ci *
5228c2ecf20Sopenharmony_ci * This function performs the same operation as vga_get(), but will return an
5238c2ecf20Sopenharmony_ci * error (-EBUSY) instead of blocking if the resources are already locked by
5248c2ecf20Sopenharmony_ci * another card. It can be called in any context
5258c2ecf20Sopenharmony_ci *
5268c2ecf20Sopenharmony_ci * On success, release the VGA resource again with vga_put().
5278c2ecf20Sopenharmony_ci *
5288c2ecf20Sopenharmony_ci * Returns:
5298c2ecf20Sopenharmony_ci *
5308c2ecf20Sopenharmony_ci * 0 on success, negative error code on failure.
5318c2ecf20Sopenharmony_ci */
5328c2ecf20Sopenharmony_cistatic int vga_tryget(struct pci_dev *pdev, unsigned int rsrc)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
5358c2ecf20Sopenharmony_ci	unsigned long flags;
5368c2ecf20Sopenharmony_ci	int rc = 0;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	vga_check_first_use();
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* The one who calls us should check for this, but lets be sure... */
5418c2ecf20Sopenharmony_ci	if (pdev == NULL)
5428c2ecf20Sopenharmony_ci		pdev = vga_default_device();
5438c2ecf20Sopenharmony_ci	if (pdev == NULL)
5448c2ecf20Sopenharmony_ci		return 0;
5458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
5468c2ecf20Sopenharmony_ci	vgadev = vgadev_find(pdev);
5478c2ecf20Sopenharmony_ci	if (vgadev == NULL) {
5488c2ecf20Sopenharmony_ci		rc = -ENODEV;
5498c2ecf20Sopenharmony_ci		goto bail;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci	if (__vga_tryget(vgadev, rsrc))
5528c2ecf20Sopenharmony_ci		rc = -EBUSY;
5538c2ecf20Sopenharmony_cibail:
5548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
5558c2ecf20Sopenharmony_ci	return rc;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci/**
5598c2ecf20Sopenharmony_ci * vga_put - release lock on legacy VGA resources
5608c2ecf20Sopenharmony_ci * @pdev: pci device of VGA card or NULL for system default
5618c2ecf20Sopenharmony_ci * @rsrc: but mask of resource to release
5628c2ecf20Sopenharmony_ci *
5638c2ecf20Sopenharmony_ci * This fuction releases resources previously locked by vga_get() or
5648c2ecf20Sopenharmony_ci * vga_tryget(). The resources aren't disabled right away, so that a subsequence
5658c2ecf20Sopenharmony_ci * vga_get() on the same card will succeed immediately. Resources have a
5668c2ecf20Sopenharmony_ci * counter, so locks are only released if the counter reaches 0.
5678c2ecf20Sopenharmony_ci */
5688c2ecf20Sopenharmony_civoid vga_put(struct pci_dev *pdev, unsigned int rsrc)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
5718c2ecf20Sopenharmony_ci	unsigned long flags;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/* The one who calls us should check for this, but lets be sure... */
5748c2ecf20Sopenharmony_ci	if (pdev == NULL)
5758c2ecf20Sopenharmony_ci		pdev = vga_default_device();
5768c2ecf20Sopenharmony_ci	if (pdev == NULL)
5778c2ecf20Sopenharmony_ci		return;
5788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
5798c2ecf20Sopenharmony_ci	vgadev = vgadev_find(pdev);
5808c2ecf20Sopenharmony_ci	if (vgadev == NULL)
5818c2ecf20Sopenharmony_ci		goto bail;
5828c2ecf20Sopenharmony_ci	__vga_put(vgadev, rsrc);
5838c2ecf20Sopenharmony_cibail:
5848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vga_put);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci/*
5898c2ecf20Sopenharmony_ci * Rules for using a bridge to control a VGA descendant decoding: if a bridge
5908c2ecf20Sopenharmony_ci * has only one VGA descendant then it can be used to control the VGA routing
5918c2ecf20Sopenharmony_ci * for that device. It should always use the bridge closest to the device to
5928c2ecf20Sopenharmony_ci * control it. If a bridge has a direct VGA descendant, but also have a sub-
5938c2ecf20Sopenharmony_ci * bridge VGA descendant then we cannot use that bridge to control the direct
5948c2ecf20Sopenharmony_ci * VGA descendant. So for every device we register, we need to iterate all
5958c2ecf20Sopenharmony_ci * its parent bridges so we can invalidate any devices using them properly.
5968c2ecf20Sopenharmony_ci */
5978c2ecf20Sopenharmony_cistatic void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	struct vga_device *same_bridge_vgadev;
6008c2ecf20Sopenharmony_ci	struct pci_bus *new_bus, *bus;
6018c2ecf20Sopenharmony_ci	struct pci_dev *new_bridge, *bridge;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	vgadev->bridge_has_one_vga = true;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (list_empty(&vga_list))
6068c2ecf20Sopenharmony_ci		return;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* okay iterate the new devices bridge hierarachy */
6098c2ecf20Sopenharmony_ci	new_bus = vgadev->pdev->bus;
6108c2ecf20Sopenharmony_ci	while (new_bus) {
6118c2ecf20Sopenharmony_ci		new_bridge = new_bus->self;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		/* go through list of devices already registered */
6148c2ecf20Sopenharmony_ci		list_for_each_entry(same_bridge_vgadev, &vga_list, list) {
6158c2ecf20Sopenharmony_ci			bus = same_bridge_vgadev->pdev->bus;
6168c2ecf20Sopenharmony_ci			bridge = bus->self;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci			/* see if the share a bridge with this device */
6198c2ecf20Sopenharmony_ci			if (new_bridge == bridge) {
6208c2ecf20Sopenharmony_ci				/*
6218c2ecf20Sopenharmony_ci				 * If their direct parent bridge is the same
6228c2ecf20Sopenharmony_ci				 * as any bridge of this device then it can't
6238c2ecf20Sopenharmony_ci				 * be used for that device.
6248c2ecf20Sopenharmony_ci				 */
6258c2ecf20Sopenharmony_ci				same_bridge_vgadev->bridge_has_one_vga = false;
6268c2ecf20Sopenharmony_ci			}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci			/*
6298c2ecf20Sopenharmony_ci			 * Now iterate the previous devices bridge hierarchy.
6308c2ecf20Sopenharmony_ci			 * If the new devices parent bridge is in the other
6318c2ecf20Sopenharmony_ci			 * devices hierarchy then we can't use it to control
6328c2ecf20Sopenharmony_ci			 * this device
6338c2ecf20Sopenharmony_ci			 */
6348c2ecf20Sopenharmony_ci			while (bus) {
6358c2ecf20Sopenharmony_ci				bridge = bus->self;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci				if (bridge && bridge == vgadev->pdev->bus->self)
6388c2ecf20Sopenharmony_ci					vgadev->bridge_has_one_vga = false;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci				bus = bus->parent;
6418c2ecf20Sopenharmony_ci			}
6428c2ecf20Sopenharmony_ci		}
6438c2ecf20Sopenharmony_ci		new_bus = new_bus->parent;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/*
6488c2ecf20Sopenharmony_ci * Currently, we assume that the "initial" setup of the system is
6498c2ecf20Sopenharmony_ci * not sane, that is we come up with conflicting devices and let
6508c2ecf20Sopenharmony_ci * the arbiter's client decides if devices decodes or not legacy
6518c2ecf20Sopenharmony_ci * things.
6528c2ecf20Sopenharmony_ci */
6538c2ecf20Sopenharmony_cistatic bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
6568c2ecf20Sopenharmony_ci	unsigned long flags;
6578c2ecf20Sopenharmony_ci	struct pci_bus *bus;
6588c2ecf20Sopenharmony_ci	struct pci_dev *bridge;
6598c2ecf20Sopenharmony_ci	u16 cmd;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	/* Only deal with VGA class devices */
6628c2ecf20Sopenharmony_ci	if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
6638c2ecf20Sopenharmony_ci		return false;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* Allocate structure */
6668c2ecf20Sopenharmony_ci	vgadev = kzalloc(sizeof(struct vga_device), GFP_KERNEL);
6678c2ecf20Sopenharmony_ci	if (vgadev == NULL) {
6688c2ecf20Sopenharmony_ci		vgaarb_err(&pdev->dev, "failed to allocate VGA arbiter data\n");
6698c2ecf20Sopenharmony_ci		/*
6708c2ecf20Sopenharmony_ci		 * What to do on allocation failure ? For now, let's just do
6718c2ecf20Sopenharmony_ci		 * nothing, I'm not sure there is anything saner to be done.
6728c2ecf20Sopenharmony_ci		 */
6738c2ecf20Sopenharmony_ci		return false;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	/* Take lock & check for duplicates */
6778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
6788c2ecf20Sopenharmony_ci	if (vgadev_find(pdev) != NULL) {
6798c2ecf20Sopenharmony_ci		BUG_ON(1);
6808c2ecf20Sopenharmony_ci		goto fail;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci	vgadev->pdev = pdev;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	/* By default, assume we decode everything */
6858c2ecf20Sopenharmony_ci	vgadev->decodes = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
6868c2ecf20Sopenharmony_ci			  VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* by default mark it as decoding */
6898c2ecf20Sopenharmony_ci	vga_decode_count++;
6908c2ecf20Sopenharmony_ci	/* Mark that we "own" resources based on our enables, we will
6918c2ecf20Sopenharmony_ci	 * clear that below if the bridge isn't forwarding
6928c2ecf20Sopenharmony_ci	 */
6938c2ecf20Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
6948c2ecf20Sopenharmony_ci	if (cmd & PCI_COMMAND_IO)
6958c2ecf20Sopenharmony_ci		vgadev->owns |= VGA_RSRC_LEGACY_IO;
6968c2ecf20Sopenharmony_ci	if (cmd & PCI_COMMAND_MEMORY)
6978c2ecf20Sopenharmony_ci		vgadev->owns |= VGA_RSRC_LEGACY_MEM;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/* Check if VGA cycles can get down to us */
7008c2ecf20Sopenharmony_ci	bus = pdev->bus;
7018c2ecf20Sopenharmony_ci	while (bus) {
7028c2ecf20Sopenharmony_ci		bridge = bus->self;
7038c2ecf20Sopenharmony_ci		if (bridge) {
7048c2ecf20Sopenharmony_ci			u16 l;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci			pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l);
7078c2ecf20Sopenharmony_ci			if (!(l & PCI_BRIDGE_CTL_VGA)) {
7088c2ecf20Sopenharmony_ci				vgadev->owns = 0;
7098c2ecf20Sopenharmony_ci				break;
7108c2ecf20Sopenharmony_ci			}
7118c2ecf20Sopenharmony_ci		}
7128c2ecf20Sopenharmony_ci		bus = bus->parent;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	/* Deal with VGA default device. Use first enabled one
7168c2ecf20Sopenharmony_ci	 * by default if arch doesn't have it's own hook
7178c2ecf20Sopenharmony_ci	 */
7188c2ecf20Sopenharmony_ci	if (vga_default == NULL &&
7198c2ecf20Sopenharmony_ci	    ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) {
7208c2ecf20Sopenharmony_ci		vgaarb_info(&pdev->dev, "setting as boot VGA device\n");
7218c2ecf20Sopenharmony_ci		vga_set_default_device(pdev);
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	vga_arbiter_check_bridge_sharing(vgadev);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	/* Add to the list */
7278c2ecf20Sopenharmony_ci	list_add_tail(&vgadev->list, &vga_list);
7288c2ecf20Sopenharmony_ci	vga_count++;
7298c2ecf20Sopenharmony_ci	vgaarb_info(&pdev->dev, "VGA device added: decodes=%s,owns=%s,locks=%s\n",
7308c2ecf20Sopenharmony_ci		vga_iostate_to_str(vgadev->decodes),
7318c2ecf20Sopenharmony_ci		vga_iostate_to_str(vgadev->owns),
7328c2ecf20Sopenharmony_ci		vga_iostate_to_str(vgadev->locks));
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
7358c2ecf20Sopenharmony_ci	return true;
7368c2ecf20Sopenharmony_cifail:
7378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
7388c2ecf20Sopenharmony_ci	kfree(vgadev);
7398c2ecf20Sopenharmony_ci	return false;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic bool vga_arbiter_del_pci_device(struct pci_dev *pdev)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
7458c2ecf20Sopenharmony_ci	unsigned long flags;
7468c2ecf20Sopenharmony_ci	bool ret = true;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
7498c2ecf20Sopenharmony_ci	vgadev = vgadev_find(pdev);
7508c2ecf20Sopenharmony_ci	if (vgadev == NULL) {
7518c2ecf20Sopenharmony_ci		ret = false;
7528c2ecf20Sopenharmony_ci		goto bail;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (vga_default == pdev)
7568c2ecf20Sopenharmony_ci		vga_set_default_device(NULL);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM))
7598c2ecf20Sopenharmony_ci		vga_decode_count--;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	/* Remove entry from list */
7628c2ecf20Sopenharmony_ci	list_del(&vgadev->list);
7638c2ecf20Sopenharmony_ci	vga_count--;
7648c2ecf20Sopenharmony_ci	/* Notify userland driver that the device is gone so it discards
7658c2ecf20Sopenharmony_ci	 * it's copies of the pci_dev pointer
7668c2ecf20Sopenharmony_ci	 */
7678c2ecf20Sopenharmony_ci	vga_arb_device_card_gone(pdev);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/* Wake up all possible waiters */
7708c2ecf20Sopenharmony_ci	wake_up_all(&vga_wait_queue);
7718c2ecf20Sopenharmony_cibail:
7728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
7738c2ecf20Sopenharmony_ci	kfree(vgadev);
7748c2ecf20Sopenharmony_ci	return ret;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci/* this is called with the lock */
7788c2ecf20Sopenharmony_cistatic inline void vga_update_device_decodes(struct vga_device *vgadev,
7798c2ecf20Sopenharmony_ci					     int new_decodes)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct device *dev = &vgadev->pdev->dev;
7828c2ecf20Sopenharmony_ci	int old_decodes, decodes_removed, decodes_unlocked;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	old_decodes = vgadev->decodes;
7858c2ecf20Sopenharmony_ci	decodes_removed = ~new_decodes & old_decodes;
7868c2ecf20Sopenharmony_ci	decodes_unlocked = vgadev->locks & decodes_removed;
7878c2ecf20Sopenharmony_ci	vgadev->decodes = new_decodes;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	vgaarb_info(dev, "changed VGA decodes: olddecodes=%s,decodes=%s:owns=%s\n",
7908c2ecf20Sopenharmony_ci		vga_iostate_to_str(old_decodes),
7918c2ecf20Sopenharmony_ci		vga_iostate_to_str(vgadev->decodes),
7928c2ecf20Sopenharmony_ci		vga_iostate_to_str(vgadev->owns));
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	/* if we removed locked decodes, lock count goes to zero, and release */
7958c2ecf20Sopenharmony_ci	if (decodes_unlocked) {
7968c2ecf20Sopenharmony_ci		if (decodes_unlocked & VGA_RSRC_LEGACY_IO)
7978c2ecf20Sopenharmony_ci			vgadev->io_lock_cnt = 0;
7988c2ecf20Sopenharmony_ci		if (decodes_unlocked & VGA_RSRC_LEGACY_MEM)
7998c2ecf20Sopenharmony_ci			vgadev->mem_lock_cnt = 0;
8008c2ecf20Sopenharmony_ci		__vga_put(vgadev, decodes_unlocked);
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/* change decodes counter */
8048c2ecf20Sopenharmony_ci	if (old_decodes & VGA_RSRC_LEGACY_MASK &&
8058c2ecf20Sopenharmony_ci	    !(new_decodes & VGA_RSRC_LEGACY_MASK))
8068c2ecf20Sopenharmony_ci		vga_decode_count--;
8078c2ecf20Sopenharmony_ci	if (!(old_decodes & VGA_RSRC_LEGACY_MASK) &&
8088c2ecf20Sopenharmony_ci	    new_decodes & VGA_RSRC_LEGACY_MASK)
8098c2ecf20Sopenharmony_ci		vga_decode_count++;
8108c2ecf20Sopenharmony_ci	vgaarb_dbg(dev, "decoding count now is: %d\n", vga_decode_count);
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic void __vga_set_legacy_decoding(struct pci_dev *pdev,
8148c2ecf20Sopenharmony_ci				      unsigned int decodes,
8158c2ecf20Sopenharmony_ci				      bool userspace)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
8188c2ecf20Sopenharmony_ci	unsigned long flags;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	decodes &= VGA_RSRC_LEGACY_MASK;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
8238c2ecf20Sopenharmony_ci	vgadev = vgadev_find(pdev);
8248c2ecf20Sopenharmony_ci	if (vgadev == NULL)
8258c2ecf20Sopenharmony_ci		goto bail;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	/* don't let userspace futz with kernel driver decodes */
8288c2ecf20Sopenharmony_ci	if (userspace && vgadev->set_vga_decode)
8298c2ecf20Sopenharmony_ci		goto bail;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	/* update the device decodes + counter */
8328c2ecf20Sopenharmony_ci	vga_update_device_decodes(vgadev, decodes);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* XXX if somebody is going from "doesn't decode" to "decodes" state
8358c2ecf20Sopenharmony_ci	 * here, additional care must be taken as we may have pending owner
8368c2ecf20Sopenharmony_ci	 * ship of non-legacy region ...
8378c2ecf20Sopenharmony_ci	 */
8388c2ecf20Sopenharmony_cibail:
8398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_civoid vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	__vga_set_legacy_decoding(pdev, decodes, false);
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vga_set_legacy_decoding);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci/**
8498c2ecf20Sopenharmony_ci * vga_client_register - register or unregister a VGA arbitration client
8508c2ecf20Sopenharmony_ci * @pdev: pci device of the VGA client
8518c2ecf20Sopenharmony_ci * @cookie: client cookie to be used in callbacks
8528c2ecf20Sopenharmony_ci * @irq_set_state: irq state change callback
8538c2ecf20Sopenharmony_ci * @set_vga_decode: vga decode change callback
8548c2ecf20Sopenharmony_ci *
8558c2ecf20Sopenharmony_ci * Clients have two callback mechanisms they can use.
8568c2ecf20Sopenharmony_ci *
8578c2ecf20Sopenharmony_ci * @irq_set_state callback: If a client can't disable its GPUs VGA
8588c2ecf20Sopenharmony_ci * resources, then we need to be able to ask it to turn off its irqs when we
8598c2ecf20Sopenharmony_ci * turn off its mem and io decoding.
8608c2ecf20Sopenharmony_ci *
8618c2ecf20Sopenharmony_ci * @set_vga_decode callback: If a client can disable its GPU VGA resource, it
8628c2ecf20Sopenharmony_ci * will get a callback from this to set the encode/decode state.
8638c2ecf20Sopenharmony_ci *
8648c2ecf20Sopenharmony_ci * Rationale: we cannot disable VGA decode resources unconditionally some single
8658c2ecf20Sopenharmony_ci * GPU laptops seem to require ACPI or BIOS access to the VGA registers to
8668c2ecf20Sopenharmony_ci * control things like backlights etc.  Hopefully newer multi-GPU laptops do
8678c2ecf20Sopenharmony_ci * something saner, and desktops won't have any special ACPI for this. The
8688c2ecf20Sopenharmony_ci * driver will get a callback when VGA arbitration is first used by userspace
8698c2ecf20Sopenharmony_ci * since some older X servers have issues.
8708c2ecf20Sopenharmony_ci *
8718c2ecf20Sopenharmony_ci * This function does not check whether a client for @pdev has been registered
8728c2ecf20Sopenharmony_ci * already.
8738c2ecf20Sopenharmony_ci *
8748c2ecf20Sopenharmony_ci * To unregister just call this function with @irq_set_state and @set_vga_decode
8758c2ecf20Sopenharmony_ci * both set to NULL for the same @pdev as originally used to register them.
8768c2ecf20Sopenharmony_ci *
8778c2ecf20Sopenharmony_ci * Returns: 0 on success, -1 on failure
8788c2ecf20Sopenharmony_ci */
8798c2ecf20Sopenharmony_ciint vga_client_register(struct pci_dev *pdev, void *cookie,
8808c2ecf20Sopenharmony_ci			void (*irq_set_state)(void *cookie, bool state),
8818c2ecf20Sopenharmony_ci			unsigned int (*set_vga_decode)(void *cookie,
8828c2ecf20Sopenharmony_ci						       bool decode))
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	int ret = -ENODEV;
8858c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
8868c2ecf20Sopenharmony_ci	unsigned long flags;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
8898c2ecf20Sopenharmony_ci	vgadev = vgadev_find(pdev);
8908c2ecf20Sopenharmony_ci	if (!vgadev)
8918c2ecf20Sopenharmony_ci		goto bail;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	vgadev->irq_set_state = irq_set_state;
8948c2ecf20Sopenharmony_ci	vgadev->set_vga_decode = set_vga_decode;
8958c2ecf20Sopenharmony_ci	vgadev->cookie = cookie;
8968c2ecf20Sopenharmony_ci	ret = 0;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cibail:
8998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
9008c2ecf20Sopenharmony_ci	return ret;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vga_client_register);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci/*
9068c2ecf20Sopenharmony_ci * Char driver implementation
9078c2ecf20Sopenharmony_ci *
9088c2ecf20Sopenharmony_ci * Semantics is:
9098c2ecf20Sopenharmony_ci *
9108c2ecf20Sopenharmony_ci *  open       : open user instance of the arbitrer. by default, it's
9118c2ecf20Sopenharmony_ci *                attached to the default VGA device of the system.
9128c2ecf20Sopenharmony_ci *
9138c2ecf20Sopenharmony_ci *  close      : close user instance, release locks
9148c2ecf20Sopenharmony_ci *
9158c2ecf20Sopenharmony_ci *  read       : return a string indicating the status of the target.
9168c2ecf20Sopenharmony_ci *                an IO state string is of the form {io,mem,io+mem,none},
9178c2ecf20Sopenharmony_ci *                mc and ic are respectively mem and io lock counts (for
9188c2ecf20Sopenharmony_ci *                debugging/diagnostic only). "decodes" indicate what the
9198c2ecf20Sopenharmony_ci *                card currently decodes, "owns" indicates what is currently
9208c2ecf20Sopenharmony_ci *                enabled on it, and "locks" indicates what is locked by this
9218c2ecf20Sopenharmony_ci *                card. If the card is unplugged, we get "invalid" then for
9228c2ecf20Sopenharmony_ci *                card_ID and an -ENODEV error is returned for any command
9238c2ecf20Sopenharmony_ci *                until a new card is targeted
9248c2ecf20Sopenharmony_ci *
9258c2ecf20Sopenharmony_ci *   "<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)"
9268c2ecf20Sopenharmony_ci *
9278c2ecf20Sopenharmony_ci * write       : write a command to the arbiter. List of commands is:
9288c2ecf20Sopenharmony_ci *
9298c2ecf20Sopenharmony_ci *   target <card_ID>   : switch target to card <card_ID> (see below)
9308c2ecf20Sopenharmony_ci *   lock <io_state>    : acquires locks on target ("none" is invalid io_state)
9318c2ecf20Sopenharmony_ci *   trylock <io_state> : non-blocking acquire locks on target
9328c2ecf20Sopenharmony_ci *   unlock <io_state>  : release locks on target
9338c2ecf20Sopenharmony_ci *   unlock all         : release all locks on target held by this user
9348c2ecf20Sopenharmony_ci *   decodes <io_state> : set the legacy decoding attributes for the card
9358c2ecf20Sopenharmony_ci *
9368c2ecf20Sopenharmony_ci * poll         : event if something change on any card (not just the target)
9378c2ecf20Sopenharmony_ci *
9388c2ecf20Sopenharmony_ci * card_ID is of the form "PCI:domain:bus:dev.fn". It can be set to "default"
9398c2ecf20Sopenharmony_ci * to go back to the system default card (TODO: not implemented yet).
9408c2ecf20Sopenharmony_ci * Currently, only PCI is supported as a prefix, but the userland API may
9418c2ecf20Sopenharmony_ci * support other bus types in the future, even if the current kernel
9428c2ecf20Sopenharmony_ci * implementation doesn't.
9438c2ecf20Sopenharmony_ci *
9448c2ecf20Sopenharmony_ci * Note about locks:
9458c2ecf20Sopenharmony_ci *
9468c2ecf20Sopenharmony_ci * The driver keeps track of which user has what locks on which card. It
9478c2ecf20Sopenharmony_ci * supports stacking, like the kernel one. This complexifies the implementation
9488c2ecf20Sopenharmony_ci * a bit, but makes the arbiter more tolerant to userspace problems and able
9498c2ecf20Sopenharmony_ci * to properly cleanup in all cases when a process dies.
9508c2ecf20Sopenharmony_ci * Currently, a max of 16 cards simultaneously can have locks issued from
9518c2ecf20Sopenharmony_ci * userspace for a given user (file descriptor instance) of the arbiter.
9528c2ecf20Sopenharmony_ci *
9538c2ecf20Sopenharmony_ci * If the device is hot-unplugged, there is a hook inside the module to notify
9548c2ecf20Sopenharmony_ci * they being added/removed in the system and automatically added/removed in
9558c2ecf20Sopenharmony_ci * the arbiter.
9568c2ecf20Sopenharmony_ci */
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci#define MAX_USER_CARDS         CONFIG_VGA_ARB_MAX_GPUS
9598c2ecf20Sopenharmony_ci#define PCI_INVALID_CARD       ((struct pci_dev *)-1UL)
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci/*
9628c2ecf20Sopenharmony_ci * Each user has an array of these, tracking which cards have locks
9638c2ecf20Sopenharmony_ci */
9648c2ecf20Sopenharmony_cistruct vga_arb_user_card {
9658c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
9668c2ecf20Sopenharmony_ci	unsigned int mem_cnt;
9678c2ecf20Sopenharmony_ci	unsigned int io_cnt;
9688c2ecf20Sopenharmony_ci};
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistruct vga_arb_private {
9718c2ecf20Sopenharmony_ci	struct list_head list;
9728c2ecf20Sopenharmony_ci	struct pci_dev *target;
9738c2ecf20Sopenharmony_ci	struct vga_arb_user_card cards[MAX_USER_CARDS];
9748c2ecf20Sopenharmony_ci	spinlock_t lock;
9758c2ecf20Sopenharmony_ci};
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic LIST_HEAD(vga_user_list);
9788c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(vga_user_lock);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci/*
9828c2ecf20Sopenharmony_ci * This function gets a string in the format: "PCI:domain:bus:dev.fn" and
9838c2ecf20Sopenharmony_ci * returns the respective values. If the string is not in this format,
9848c2ecf20Sopenharmony_ci * it returns 0.
9858c2ecf20Sopenharmony_ci */
9868c2ecf20Sopenharmony_cistatic int vga_pci_str_to_vars(char *buf, int count, unsigned int *domain,
9878c2ecf20Sopenharmony_ci			       unsigned int *bus, unsigned int *devfn)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	int n;
9908c2ecf20Sopenharmony_ci	unsigned int slot, func;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	n = sscanf(buf, "PCI:%x:%x:%x.%x", domain, bus, &slot, &func);
9948c2ecf20Sopenharmony_ci	if (n != 4)
9958c2ecf20Sopenharmony_ci		return 0;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	*devfn = PCI_DEVFN(slot, func);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return 1;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic ssize_t vga_arb_read(struct file *file, char __user *buf,
10038c2ecf20Sopenharmony_ci			    size_t count, loff_t *ppos)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	struct vga_arb_private *priv = file->private_data;
10068c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
10078c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
10088c2ecf20Sopenharmony_ci	unsigned long flags;
10098c2ecf20Sopenharmony_ci	size_t len;
10108c2ecf20Sopenharmony_ci	int rc;
10118c2ecf20Sopenharmony_ci	char *lbuf;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	lbuf = kmalloc(1024, GFP_KERNEL);
10148c2ecf20Sopenharmony_ci	if (lbuf == NULL)
10158c2ecf20Sopenharmony_ci		return -ENOMEM;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* Shields against vga_arb_device_card_gone (pci_dev going
10188c2ecf20Sopenharmony_ci	 * away), and allows access to vga list
10198c2ecf20Sopenharmony_ci	 */
10208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	/* If we are targeting the default, use it */
10238c2ecf20Sopenharmony_ci	pdev = priv->target;
10248c2ecf20Sopenharmony_ci	if (pdev == NULL || pdev == PCI_INVALID_CARD) {
10258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vga_lock, flags);
10268c2ecf20Sopenharmony_ci		len = sprintf(lbuf, "invalid");
10278c2ecf20Sopenharmony_ci		goto done;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	/* Find card vgadev structure */
10318c2ecf20Sopenharmony_ci	vgadev = vgadev_find(pdev);
10328c2ecf20Sopenharmony_ci	if (vgadev == NULL) {
10338c2ecf20Sopenharmony_ci		/* Wow, it's not in the list, that shouldn't happen,
10348c2ecf20Sopenharmony_ci		 * let's fix us up and return invalid card
10358c2ecf20Sopenharmony_ci		 */
10368c2ecf20Sopenharmony_ci		if (pdev == priv->target)
10378c2ecf20Sopenharmony_ci			vga_arb_device_card_gone(pdev);
10388c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vga_lock, flags);
10398c2ecf20Sopenharmony_ci		len = sprintf(lbuf, "invalid");
10408c2ecf20Sopenharmony_ci		goto done;
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	/* Fill the buffer with infos */
10448c2ecf20Sopenharmony_ci	len = snprintf(lbuf, 1024,
10458c2ecf20Sopenharmony_ci		       "count:%d,PCI:%s,decodes=%s,owns=%s,locks=%s(%d:%d)\n",
10468c2ecf20Sopenharmony_ci		       vga_decode_count, pci_name(pdev),
10478c2ecf20Sopenharmony_ci		       vga_iostate_to_str(vgadev->decodes),
10488c2ecf20Sopenharmony_ci		       vga_iostate_to_str(vgadev->owns),
10498c2ecf20Sopenharmony_ci		       vga_iostate_to_str(vgadev->locks),
10508c2ecf20Sopenharmony_ci		       vgadev->io_lock_cnt, vgadev->mem_lock_cnt);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
10538c2ecf20Sopenharmony_cidone:
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	/* Copy that to user */
10568c2ecf20Sopenharmony_ci	if (len > count)
10578c2ecf20Sopenharmony_ci		len = count;
10588c2ecf20Sopenharmony_ci	rc = copy_to_user(buf, lbuf, len);
10598c2ecf20Sopenharmony_ci	kfree(lbuf);
10608c2ecf20Sopenharmony_ci	if (rc)
10618c2ecf20Sopenharmony_ci		return -EFAULT;
10628c2ecf20Sopenharmony_ci	return len;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci/*
10668c2ecf20Sopenharmony_ci * TODO: To avoid parsing inside kernel and to improve the speed we may
10678c2ecf20Sopenharmony_ci * consider use ioctl here
10688c2ecf20Sopenharmony_ci */
10698c2ecf20Sopenharmony_cistatic ssize_t vga_arb_write(struct file *file, const char __user *buf,
10708c2ecf20Sopenharmony_ci			     size_t count, loff_t *ppos)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct vga_arb_private *priv = file->private_data;
10738c2ecf20Sopenharmony_ci	struct vga_arb_user_card *uc = NULL;
10748c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	unsigned int io_state;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	char kbuf[64], *curr_pos;
10798c2ecf20Sopenharmony_ci	size_t remaining = count;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	int ret_val;
10828c2ecf20Sopenharmony_ci	int i;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	if (count >= sizeof(kbuf))
10858c2ecf20Sopenharmony_ci		return -EINVAL;
10868c2ecf20Sopenharmony_ci	if (copy_from_user(kbuf, buf, count))
10878c2ecf20Sopenharmony_ci		return -EFAULT;
10888c2ecf20Sopenharmony_ci	curr_pos = kbuf;
10898c2ecf20Sopenharmony_ci	kbuf[count] = '\0';	/* Just to make sure... */
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	if (strncmp(curr_pos, "lock ", 5) == 0) {
10928c2ecf20Sopenharmony_ci		curr_pos += 5;
10938c2ecf20Sopenharmony_ci		remaining -= 5;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		pr_debug("client 0x%p called 'lock'\n", priv);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci		if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) {
10988c2ecf20Sopenharmony_ci			ret_val = -EPROTO;
10998c2ecf20Sopenharmony_ci			goto done;
11008c2ecf20Sopenharmony_ci		}
11018c2ecf20Sopenharmony_ci		if (io_state == VGA_RSRC_NONE) {
11028c2ecf20Sopenharmony_ci			ret_val = -EPROTO;
11038c2ecf20Sopenharmony_ci			goto done;
11048c2ecf20Sopenharmony_ci		}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci		pdev = priv->target;
11078c2ecf20Sopenharmony_ci		if (priv->target == NULL) {
11088c2ecf20Sopenharmony_ci			ret_val = -ENODEV;
11098c2ecf20Sopenharmony_ci			goto done;
11108c2ecf20Sopenharmony_ci		}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci		vga_get_uninterruptible(pdev, io_state);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci		/* Update the client's locks lists... */
11158c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_USER_CARDS; i++) {
11168c2ecf20Sopenharmony_ci			if (priv->cards[i].pdev == pdev) {
11178c2ecf20Sopenharmony_ci				if (io_state & VGA_RSRC_LEGACY_IO)
11188c2ecf20Sopenharmony_ci					priv->cards[i].io_cnt++;
11198c2ecf20Sopenharmony_ci				if (io_state & VGA_RSRC_LEGACY_MEM)
11208c2ecf20Sopenharmony_ci					priv->cards[i].mem_cnt++;
11218c2ecf20Sopenharmony_ci				break;
11228c2ecf20Sopenharmony_ci			}
11238c2ecf20Sopenharmony_ci		}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci		ret_val = count;
11268c2ecf20Sopenharmony_ci		goto done;
11278c2ecf20Sopenharmony_ci	} else if (strncmp(curr_pos, "unlock ", 7) == 0) {
11288c2ecf20Sopenharmony_ci		curr_pos += 7;
11298c2ecf20Sopenharmony_ci		remaining -= 7;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		pr_debug("client 0x%p called 'unlock'\n", priv);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci		if (strncmp(curr_pos, "all", 3) == 0)
11348c2ecf20Sopenharmony_ci			io_state = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
11358c2ecf20Sopenharmony_ci		else {
11368c2ecf20Sopenharmony_ci			if (!vga_str_to_iostate
11378c2ecf20Sopenharmony_ci			    (curr_pos, remaining, &io_state)) {
11388c2ecf20Sopenharmony_ci				ret_val = -EPROTO;
11398c2ecf20Sopenharmony_ci				goto done;
11408c2ecf20Sopenharmony_ci			}
11418c2ecf20Sopenharmony_ci			/* TODO: Add this?
11428c2ecf20Sopenharmony_ci			   if (io_state == VGA_RSRC_NONE) {
11438c2ecf20Sopenharmony_ci			   ret_val = -EPROTO;
11448c2ecf20Sopenharmony_ci			   goto done;
11458c2ecf20Sopenharmony_ci			   }
11468c2ecf20Sopenharmony_ci			  */
11478c2ecf20Sopenharmony_ci		}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci		pdev = priv->target;
11508c2ecf20Sopenharmony_ci		if (priv->target == NULL) {
11518c2ecf20Sopenharmony_ci			ret_val = -ENODEV;
11528c2ecf20Sopenharmony_ci			goto done;
11538c2ecf20Sopenharmony_ci		}
11548c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_USER_CARDS; i++) {
11558c2ecf20Sopenharmony_ci			if (priv->cards[i].pdev == pdev)
11568c2ecf20Sopenharmony_ci				uc = &priv->cards[i];
11578c2ecf20Sopenharmony_ci		}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		if (!uc) {
11608c2ecf20Sopenharmony_ci			ret_val = -EINVAL;
11618c2ecf20Sopenharmony_ci			goto done;
11628c2ecf20Sopenharmony_ci		}
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0) {
11658c2ecf20Sopenharmony_ci			ret_val = -EINVAL;
11668c2ecf20Sopenharmony_ci			goto done;
11678c2ecf20Sopenharmony_ci		}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0) {
11708c2ecf20Sopenharmony_ci			ret_val = -EINVAL;
11718c2ecf20Sopenharmony_ci			goto done;
11728c2ecf20Sopenharmony_ci		}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci		vga_put(pdev, io_state);
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_IO)
11778c2ecf20Sopenharmony_ci			uc->io_cnt--;
11788c2ecf20Sopenharmony_ci		if (io_state & VGA_RSRC_LEGACY_MEM)
11798c2ecf20Sopenharmony_ci			uc->mem_cnt--;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		ret_val = count;
11828c2ecf20Sopenharmony_ci		goto done;
11838c2ecf20Sopenharmony_ci	} else if (strncmp(curr_pos, "trylock ", 8) == 0) {
11848c2ecf20Sopenharmony_ci		curr_pos += 8;
11858c2ecf20Sopenharmony_ci		remaining -= 8;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		pr_debug("client 0x%p called 'trylock'\n", priv);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci		if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) {
11908c2ecf20Sopenharmony_ci			ret_val = -EPROTO;
11918c2ecf20Sopenharmony_ci			goto done;
11928c2ecf20Sopenharmony_ci		}
11938c2ecf20Sopenharmony_ci		/* TODO: Add this?
11948c2ecf20Sopenharmony_ci		   if (io_state == VGA_RSRC_NONE) {
11958c2ecf20Sopenharmony_ci		   ret_val = -EPROTO;
11968c2ecf20Sopenharmony_ci		   goto done;
11978c2ecf20Sopenharmony_ci		   }
11988c2ecf20Sopenharmony_ci		 */
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci		pdev = priv->target;
12018c2ecf20Sopenharmony_ci		if (priv->target == NULL) {
12028c2ecf20Sopenharmony_ci			ret_val = -ENODEV;
12038c2ecf20Sopenharmony_ci			goto done;
12048c2ecf20Sopenharmony_ci		}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		if (vga_tryget(pdev, io_state)) {
12078c2ecf20Sopenharmony_ci			/* Update the client's locks lists... */
12088c2ecf20Sopenharmony_ci			for (i = 0; i < MAX_USER_CARDS; i++) {
12098c2ecf20Sopenharmony_ci				if (priv->cards[i].pdev == pdev) {
12108c2ecf20Sopenharmony_ci					if (io_state & VGA_RSRC_LEGACY_IO)
12118c2ecf20Sopenharmony_ci						priv->cards[i].io_cnt++;
12128c2ecf20Sopenharmony_ci					if (io_state & VGA_RSRC_LEGACY_MEM)
12138c2ecf20Sopenharmony_ci						priv->cards[i].mem_cnt++;
12148c2ecf20Sopenharmony_ci					break;
12158c2ecf20Sopenharmony_ci				}
12168c2ecf20Sopenharmony_ci			}
12178c2ecf20Sopenharmony_ci			ret_val = count;
12188c2ecf20Sopenharmony_ci			goto done;
12198c2ecf20Sopenharmony_ci		} else {
12208c2ecf20Sopenharmony_ci			ret_val = -EBUSY;
12218c2ecf20Sopenharmony_ci			goto done;
12228c2ecf20Sopenharmony_ci		}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	} else if (strncmp(curr_pos, "target ", 7) == 0) {
12258c2ecf20Sopenharmony_ci		unsigned int domain, bus, devfn;
12268c2ecf20Sopenharmony_ci		struct vga_device *vgadev;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci		curr_pos += 7;
12298c2ecf20Sopenharmony_ci		remaining -= 7;
12308c2ecf20Sopenharmony_ci		pr_debug("client 0x%p called 'target'\n", priv);
12318c2ecf20Sopenharmony_ci		/* if target is default */
12328c2ecf20Sopenharmony_ci		if (!strncmp(curr_pos, "default", 7))
12338c2ecf20Sopenharmony_ci			pdev = pci_dev_get(vga_default_device());
12348c2ecf20Sopenharmony_ci		else {
12358c2ecf20Sopenharmony_ci			if (!vga_pci_str_to_vars(curr_pos, remaining,
12368c2ecf20Sopenharmony_ci						 &domain, &bus, &devfn)) {
12378c2ecf20Sopenharmony_ci				ret_val = -EPROTO;
12388c2ecf20Sopenharmony_ci				goto done;
12398c2ecf20Sopenharmony_ci			}
12408c2ecf20Sopenharmony_ci			pdev = pci_get_domain_bus_and_slot(domain, bus, devfn);
12418c2ecf20Sopenharmony_ci			if (!pdev) {
12428c2ecf20Sopenharmony_ci				pr_debug("invalid PCI address %04x:%02x:%02x.%x\n",
12438c2ecf20Sopenharmony_ci					 domain, bus, PCI_SLOT(devfn),
12448c2ecf20Sopenharmony_ci					 PCI_FUNC(devfn));
12458c2ecf20Sopenharmony_ci				ret_val = -ENODEV;
12468c2ecf20Sopenharmony_ci				goto done;
12478c2ecf20Sopenharmony_ci			}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci			pr_debug("%s ==> %04x:%02x:%02x.%x pdev %p\n", curr_pos,
12508c2ecf20Sopenharmony_ci				domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
12518c2ecf20Sopenharmony_ci				pdev);
12528c2ecf20Sopenharmony_ci		}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		vgadev = vgadev_find(pdev);
12558c2ecf20Sopenharmony_ci		pr_debug("vgadev %p\n", vgadev);
12568c2ecf20Sopenharmony_ci		if (vgadev == NULL) {
12578c2ecf20Sopenharmony_ci			if (pdev) {
12588c2ecf20Sopenharmony_ci				vgaarb_dbg(&pdev->dev, "not a VGA device\n");
12598c2ecf20Sopenharmony_ci				pci_dev_put(pdev);
12608c2ecf20Sopenharmony_ci			}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci			ret_val = -ENODEV;
12638c2ecf20Sopenharmony_ci			goto done;
12648c2ecf20Sopenharmony_ci		}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci		priv->target = pdev;
12678c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_USER_CARDS; i++) {
12688c2ecf20Sopenharmony_ci			if (priv->cards[i].pdev == pdev)
12698c2ecf20Sopenharmony_ci				break;
12708c2ecf20Sopenharmony_ci			if (priv->cards[i].pdev == NULL) {
12718c2ecf20Sopenharmony_ci				priv->cards[i].pdev = pdev;
12728c2ecf20Sopenharmony_ci				priv->cards[i].io_cnt = 0;
12738c2ecf20Sopenharmony_ci				priv->cards[i].mem_cnt = 0;
12748c2ecf20Sopenharmony_ci				break;
12758c2ecf20Sopenharmony_ci			}
12768c2ecf20Sopenharmony_ci		}
12778c2ecf20Sopenharmony_ci		if (i == MAX_USER_CARDS) {
12788c2ecf20Sopenharmony_ci			vgaarb_dbg(&pdev->dev, "maximum user cards (%d) number reached, ignoring this one!\n",
12798c2ecf20Sopenharmony_ci				MAX_USER_CARDS);
12808c2ecf20Sopenharmony_ci			pci_dev_put(pdev);
12818c2ecf20Sopenharmony_ci			/* XXX: which value to return? */
12828c2ecf20Sopenharmony_ci			ret_val =  -ENOMEM;
12838c2ecf20Sopenharmony_ci			goto done;
12848c2ecf20Sopenharmony_ci		}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci		ret_val = count;
12878c2ecf20Sopenharmony_ci		pci_dev_put(pdev);
12888c2ecf20Sopenharmony_ci		goto done;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	} else if (strncmp(curr_pos, "decodes ", 8) == 0) {
12928c2ecf20Sopenharmony_ci		curr_pos += 8;
12938c2ecf20Sopenharmony_ci		remaining -= 8;
12948c2ecf20Sopenharmony_ci		pr_debug("client 0x%p called 'decodes'\n", priv);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci		if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) {
12978c2ecf20Sopenharmony_ci			ret_val = -EPROTO;
12988c2ecf20Sopenharmony_ci			goto done;
12998c2ecf20Sopenharmony_ci		}
13008c2ecf20Sopenharmony_ci		pdev = priv->target;
13018c2ecf20Sopenharmony_ci		if (priv->target == NULL) {
13028c2ecf20Sopenharmony_ci			ret_val = -ENODEV;
13038c2ecf20Sopenharmony_ci			goto done;
13048c2ecf20Sopenharmony_ci		}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci		__vga_set_legacy_decoding(pdev, io_state, true);
13078c2ecf20Sopenharmony_ci		ret_val = count;
13088c2ecf20Sopenharmony_ci		goto done;
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci	/* If we got here, the message written is not part of the protocol! */
13118c2ecf20Sopenharmony_ci	return -EPROTO;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_cidone:
13148c2ecf20Sopenharmony_ci	return ret_val;
13158c2ecf20Sopenharmony_ci}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_cistatic __poll_t vga_arb_fpoll(struct file *file, poll_table *wait)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci	pr_debug("%s\n", __func__);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	poll_wait(file, &vga_wait_queue, wait);
13228c2ecf20Sopenharmony_ci	return EPOLLIN;
13238c2ecf20Sopenharmony_ci}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_cistatic int vga_arb_open(struct inode *inode, struct file *file)
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	struct vga_arb_private *priv;
13288c2ecf20Sopenharmony_ci	unsigned long flags;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	pr_debug("%s\n", __func__);
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
13338c2ecf20Sopenharmony_ci	if (priv == NULL)
13348c2ecf20Sopenharmony_ci		return -ENOMEM;
13358c2ecf20Sopenharmony_ci	spin_lock_init(&priv->lock);
13368c2ecf20Sopenharmony_ci	file->private_data = priv;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_user_lock, flags);
13398c2ecf20Sopenharmony_ci	list_add(&priv->list, &vga_user_list);
13408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_user_lock, flags);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	/* Set the client' lists of locks */
13438c2ecf20Sopenharmony_ci	priv->target = vga_default_device(); /* Maybe this is still null! */
13448c2ecf20Sopenharmony_ci	priv->cards[0].pdev = priv->target;
13458c2ecf20Sopenharmony_ci	priv->cards[0].io_cnt = 0;
13468c2ecf20Sopenharmony_ci	priv->cards[0].mem_cnt = 0;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	return 0;
13508c2ecf20Sopenharmony_ci}
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_cistatic int vga_arb_release(struct inode *inode, struct file *file)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	struct vga_arb_private *priv = file->private_data;
13558c2ecf20Sopenharmony_ci	struct vga_arb_user_card *uc;
13568c2ecf20Sopenharmony_ci	unsigned long flags;
13578c2ecf20Sopenharmony_ci	int i;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	pr_debug("%s\n", __func__);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_user_lock, flags);
13628c2ecf20Sopenharmony_ci	list_del(&priv->list);
13638c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_USER_CARDS; i++) {
13648c2ecf20Sopenharmony_ci		uc = &priv->cards[i];
13658c2ecf20Sopenharmony_ci		if (uc->pdev == NULL)
13668c2ecf20Sopenharmony_ci			continue;
13678c2ecf20Sopenharmony_ci		vgaarb_dbg(&uc->pdev->dev, "uc->io_cnt == %d, uc->mem_cnt == %d\n",
13688c2ecf20Sopenharmony_ci			uc->io_cnt, uc->mem_cnt);
13698c2ecf20Sopenharmony_ci		while (uc->io_cnt--)
13708c2ecf20Sopenharmony_ci			vga_put(uc->pdev, VGA_RSRC_LEGACY_IO);
13718c2ecf20Sopenharmony_ci		while (uc->mem_cnt--)
13728c2ecf20Sopenharmony_ci			vga_put(uc->pdev, VGA_RSRC_LEGACY_MEM);
13738c2ecf20Sopenharmony_ci	}
13748c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_user_lock, flags);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	kfree(priv);
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	return 0;
13798c2ecf20Sopenharmony_ci}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_cistatic void vga_arb_device_card_gone(struct pci_dev *pdev)
13828c2ecf20Sopenharmony_ci{
13838c2ecf20Sopenharmony_ci}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci/*
13868c2ecf20Sopenharmony_ci * callback any registered clients to let them know we have a
13878c2ecf20Sopenharmony_ci * change in VGA cards
13888c2ecf20Sopenharmony_ci */
13898c2ecf20Sopenharmony_cistatic void vga_arbiter_notify_clients(void)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
13928c2ecf20Sopenharmony_ci	unsigned long flags;
13938c2ecf20Sopenharmony_ci	uint32_t new_decodes;
13948c2ecf20Sopenharmony_ci	bool new_state;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	if (!vga_arbiter_used)
13978c2ecf20Sopenharmony_ci		return;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vga_lock, flags);
14008c2ecf20Sopenharmony_ci	list_for_each_entry(vgadev, &vga_list, list) {
14018c2ecf20Sopenharmony_ci		if (vga_count > 1)
14028c2ecf20Sopenharmony_ci			new_state = false;
14038c2ecf20Sopenharmony_ci		else
14048c2ecf20Sopenharmony_ci			new_state = true;
14058c2ecf20Sopenharmony_ci		if (vgadev->set_vga_decode) {
14068c2ecf20Sopenharmony_ci			new_decodes = vgadev->set_vga_decode(vgadev->cookie,
14078c2ecf20Sopenharmony_ci							     new_state);
14088c2ecf20Sopenharmony_ci			vga_update_device_decodes(vgadev, new_decodes);
14098c2ecf20Sopenharmony_ci		}
14108c2ecf20Sopenharmony_ci	}
14118c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vga_lock, flags);
14128c2ecf20Sopenharmony_ci}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_cistatic int pci_notify(struct notifier_block *nb, unsigned long action,
14158c2ecf20Sopenharmony_ci		      void *data)
14168c2ecf20Sopenharmony_ci{
14178c2ecf20Sopenharmony_ci	struct device *dev = data;
14188c2ecf20Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
14198c2ecf20Sopenharmony_ci	bool notify = false;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	vgaarb_dbg(dev, "%s\n", __func__);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	/* For now we're only intereted in devices added and removed. I didn't
14248c2ecf20Sopenharmony_ci	 * test this thing here, so someone needs to double check for the
14258c2ecf20Sopenharmony_ci	 * cases of hotplugable vga cards. */
14268c2ecf20Sopenharmony_ci	if (action == BUS_NOTIFY_ADD_DEVICE)
14278c2ecf20Sopenharmony_ci		notify = vga_arbiter_add_pci_device(pdev);
14288c2ecf20Sopenharmony_ci	else if (action == BUS_NOTIFY_DEL_DEVICE)
14298c2ecf20Sopenharmony_ci		notify = vga_arbiter_del_pci_device(pdev);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	if (notify)
14328c2ecf20Sopenharmony_ci		vga_arbiter_notify_clients();
14338c2ecf20Sopenharmony_ci	return 0;
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_cistatic struct notifier_block pci_notifier = {
14378c2ecf20Sopenharmony_ci	.notifier_call = pci_notify,
14388c2ecf20Sopenharmony_ci};
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_cistatic const struct file_operations vga_arb_device_fops = {
14418c2ecf20Sopenharmony_ci	.read = vga_arb_read,
14428c2ecf20Sopenharmony_ci	.write = vga_arb_write,
14438c2ecf20Sopenharmony_ci	.poll = vga_arb_fpoll,
14448c2ecf20Sopenharmony_ci	.open = vga_arb_open,
14458c2ecf20Sopenharmony_ci	.release = vga_arb_release,
14468c2ecf20Sopenharmony_ci	.llseek = noop_llseek,
14478c2ecf20Sopenharmony_ci};
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_cistatic struct miscdevice vga_arb_device = {
14508c2ecf20Sopenharmony_ci	MISC_DYNAMIC_MINOR, "vga_arbiter", &vga_arb_device_fops
14518c2ecf20Sopenharmony_ci};
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic void __init vga_arb_select_default_device(void)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
14568c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) || defined(CONFIG_IA64)
14598c2ecf20Sopenharmony_ci	u64 base = screen_info.lfb_base;
14608c2ecf20Sopenharmony_ci	u64 size = screen_info.lfb_size;
14618c2ecf20Sopenharmony_ci	u64 limit;
14628c2ecf20Sopenharmony_ci	resource_size_t start, end;
14638c2ecf20Sopenharmony_ci	unsigned long flags;
14648c2ecf20Sopenharmony_ci	int i;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
14678c2ecf20Sopenharmony_ci		base |= (u64)screen_info.ext_lfb_base << 32;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	limit = base + size;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	list_for_each_entry(vgadev, &vga_list, list) {
14728c2ecf20Sopenharmony_ci		struct device *dev = &vgadev->pdev->dev;
14738c2ecf20Sopenharmony_ci		/*
14748c2ecf20Sopenharmony_ci		 * Override vga_arbiter_add_pci_device()'s I/O based detection
14758c2ecf20Sopenharmony_ci		 * as it may take the wrong device (e.g. on Apple system under
14768c2ecf20Sopenharmony_ci		 * EFI).
14778c2ecf20Sopenharmony_ci		 *
14788c2ecf20Sopenharmony_ci		 * Select the device owning the boot framebuffer if there is
14798c2ecf20Sopenharmony_ci		 * one.
14808c2ecf20Sopenharmony_ci		 */
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci		/* Does firmware framebuffer belong to us? */
14838c2ecf20Sopenharmony_ci		for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
14848c2ecf20Sopenharmony_ci			flags = pci_resource_flags(vgadev->pdev, i);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci			if ((flags & IORESOURCE_MEM) == 0)
14878c2ecf20Sopenharmony_ci				continue;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci			start = pci_resource_start(vgadev->pdev, i);
14908c2ecf20Sopenharmony_ci			end  = pci_resource_end(vgadev->pdev, i);
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci			if (!start || !end)
14938c2ecf20Sopenharmony_ci				continue;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci			if (base < start || limit >= end)
14968c2ecf20Sopenharmony_ci				continue;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci			if (!vga_default_device())
14998c2ecf20Sopenharmony_ci				vgaarb_info(dev, "setting as boot device\n");
15008c2ecf20Sopenharmony_ci			else if (vgadev->pdev != vga_default_device())
15018c2ecf20Sopenharmony_ci				vgaarb_info(dev, "overriding boot device\n");
15028c2ecf20Sopenharmony_ci			vga_set_default_device(vgadev->pdev);
15038c2ecf20Sopenharmony_ci		}
15048c2ecf20Sopenharmony_ci	}
15058c2ecf20Sopenharmony_ci#endif
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	if (!vga_default_device()) {
15088c2ecf20Sopenharmony_ci		list_for_each_entry(vgadev, &vga_list, list) {
15098c2ecf20Sopenharmony_ci			struct device *dev = &vgadev->pdev->dev;
15108c2ecf20Sopenharmony_ci			u16 cmd;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci			pdev = vgadev->pdev;
15138c2ecf20Sopenharmony_ci			pci_read_config_word(pdev, PCI_COMMAND, &cmd);
15148c2ecf20Sopenharmony_ci			if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
15158c2ecf20Sopenharmony_ci				vgaarb_info(dev, "setting as boot device (VGA legacy resources not available)\n");
15168c2ecf20Sopenharmony_ci				vga_set_default_device(pdev);
15178c2ecf20Sopenharmony_ci				break;
15188c2ecf20Sopenharmony_ci			}
15198c2ecf20Sopenharmony_ci		}
15208c2ecf20Sopenharmony_ci	}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	if (!vga_default_device()) {
15238c2ecf20Sopenharmony_ci		vgadev = list_first_entry_or_null(&vga_list,
15248c2ecf20Sopenharmony_ci						  struct vga_device, list);
15258c2ecf20Sopenharmony_ci		if (vgadev) {
15268c2ecf20Sopenharmony_ci			struct device *dev = &vgadev->pdev->dev;
15278c2ecf20Sopenharmony_ci			vgaarb_info(dev, "setting as boot device (VGA legacy resources not available)\n");
15288c2ecf20Sopenharmony_ci			vga_set_default_device(vgadev->pdev);
15298c2ecf20Sopenharmony_ci		}
15308c2ecf20Sopenharmony_ci	}
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_cistatic int __init vga_arb_device_init(void)
15348c2ecf20Sopenharmony_ci{
15358c2ecf20Sopenharmony_ci	int rc;
15368c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
15378c2ecf20Sopenharmony_ci	struct vga_device *vgadev;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	rc = misc_register(&vga_arb_device);
15408c2ecf20Sopenharmony_ci	if (rc < 0)
15418c2ecf20Sopenharmony_ci		pr_err("error %d registering device\n", rc);
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	bus_register_notifier(&pci_bus_type, &pci_notifier);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	/* We add all PCI devices satisfying VGA class in the arbiter by
15468c2ecf20Sopenharmony_ci	 * default */
15478c2ecf20Sopenharmony_ci	pdev = NULL;
15488c2ecf20Sopenharmony_ci	while ((pdev =
15498c2ecf20Sopenharmony_ci		pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
15508c2ecf20Sopenharmony_ci			       PCI_ANY_ID, pdev)) != NULL)
15518c2ecf20Sopenharmony_ci		vga_arbiter_add_pci_device(pdev);
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	list_for_each_entry(vgadev, &vga_list, list) {
15548c2ecf20Sopenharmony_ci		struct device *dev = &vgadev->pdev->dev;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci		if (vgadev->bridge_has_one_vga)
15578c2ecf20Sopenharmony_ci			vgaarb_info(dev, "bridge control possible\n");
15588c2ecf20Sopenharmony_ci		else
15598c2ecf20Sopenharmony_ci			vgaarb_info(dev, "no bridge control possible\n");
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	vga_arb_select_default_device();
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	pr_info("loaded\n");
15658c2ecf20Sopenharmony_ci	return rc;
15668c2ecf20Sopenharmony_ci}
15678c2ecf20Sopenharmony_cisubsys_initcall(vga_arb_device_init);
1568