11d38fe6eSBjorn Helgaas /* 21d38fe6eSBjorn Helgaas * vgaarb.c: Implements the VGA arbitration. For details refer to 31d38fe6eSBjorn Helgaas * Documentation/gpu/vgaarbiter.rst 41d38fe6eSBjorn Helgaas * 51d38fe6eSBjorn Helgaas * 61d38fe6eSBjorn Helgaas * (C) Copyright 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org> 71d38fe6eSBjorn Helgaas * (C) Copyright 2007 Paulo R. Zanoni <przanoni@gmail.com> 81d38fe6eSBjorn Helgaas * (C) Copyright 2007, 2009 Tiago Vignatti <vignatti@freedesktop.org> 91d38fe6eSBjorn Helgaas * 101d38fe6eSBjorn Helgaas * Permission is hereby granted, free of charge, to any person obtaining a 111d38fe6eSBjorn Helgaas * copy of this software and associated documentation files (the "Software"), 121d38fe6eSBjorn Helgaas * to deal in the Software without restriction, including without limitation 131d38fe6eSBjorn Helgaas * the rights to use, copy, modify, merge, publish, distribute, sublicense, 141d38fe6eSBjorn Helgaas * and/or sell copies of the Software, and to permit persons to whom the 151d38fe6eSBjorn Helgaas * Software is furnished to do so, subject to the following conditions: 161d38fe6eSBjorn Helgaas * 171d38fe6eSBjorn Helgaas * The above copyright notice and this permission notice (including the next 181d38fe6eSBjorn Helgaas * paragraph) shall be included in all copies or substantial portions of the 191d38fe6eSBjorn Helgaas * Software. 201d38fe6eSBjorn Helgaas * 211d38fe6eSBjorn Helgaas * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 221d38fe6eSBjorn Helgaas * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 231d38fe6eSBjorn Helgaas * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 241d38fe6eSBjorn Helgaas * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 251d38fe6eSBjorn Helgaas * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 261d38fe6eSBjorn Helgaas * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 271d38fe6eSBjorn Helgaas * DEALINGS 281d38fe6eSBjorn Helgaas * IN THE SOFTWARE. 291d38fe6eSBjorn Helgaas * 301d38fe6eSBjorn Helgaas */ 311d38fe6eSBjorn Helgaas 321d38fe6eSBjorn Helgaas #define pr_fmt(fmt) "vgaarb: " fmt 331d38fe6eSBjorn Helgaas 341d38fe6eSBjorn Helgaas #define vgaarb_dbg(dev, fmt, arg...) dev_dbg(dev, "vgaarb: " fmt, ##arg) 351d38fe6eSBjorn Helgaas #define vgaarb_info(dev, fmt, arg...) dev_info(dev, "vgaarb: " fmt, ##arg) 361d38fe6eSBjorn Helgaas #define vgaarb_err(dev, fmt, arg...) dev_err(dev, "vgaarb: " fmt, ##arg) 371d38fe6eSBjorn Helgaas 381d38fe6eSBjorn Helgaas #include <linux/module.h> 391d38fe6eSBjorn Helgaas #include <linux/kernel.h> 401d38fe6eSBjorn Helgaas #include <linux/pci.h> 411d38fe6eSBjorn Helgaas #include <linux/errno.h> 421d38fe6eSBjorn Helgaas #include <linux/init.h> 431d38fe6eSBjorn Helgaas #include <linux/list.h> 441d38fe6eSBjorn Helgaas #include <linux/sched/signal.h> 451d38fe6eSBjorn Helgaas #include <linux/wait.h> 461d38fe6eSBjorn Helgaas #include <linux/spinlock.h> 471d38fe6eSBjorn Helgaas #include <linux/poll.h> 481d38fe6eSBjorn Helgaas #include <linux/miscdevice.h> 491d38fe6eSBjorn Helgaas #include <linux/slab.h> 501d38fe6eSBjorn Helgaas #include <linux/screen_info.h> 511d38fe6eSBjorn Helgaas #include <linux/vt.h> 521d38fe6eSBjorn Helgaas #include <linux/console.h> 531d38fe6eSBjorn Helgaas #include <linux/acpi.h> 541d38fe6eSBjorn Helgaas 551d38fe6eSBjorn Helgaas #include <linux/uaccess.h> 561d38fe6eSBjorn Helgaas 571d38fe6eSBjorn Helgaas #include <linux/vgaarb.h> 581d38fe6eSBjorn Helgaas 591d38fe6eSBjorn Helgaas static void vga_arbiter_notify_clients(void); 601d38fe6eSBjorn Helgaas /* 611d38fe6eSBjorn Helgaas * We keep a list of all vga devices in the system to speed 621d38fe6eSBjorn Helgaas * up the various operations of the arbiter 631d38fe6eSBjorn Helgaas */ 641d38fe6eSBjorn Helgaas struct vga_device { 651d38fe6eSBjorn Helgaas struct list_head list; 661d38fe6eSBjorn Helgaas struct pci_dev *pdev; 671d38fe6eSBjorn Helgaas unsigned int decodes; /* what does it decodes */ 681d38fe6eSBjorn Helgaas unsigned int owns; /* what does it owns */ 691d38fe6eSBjorn Helgaas unsigned int locks; /* what does it locks */ 701d38fe6eSBjorn Helgaas unsigned int io_lock_cnt; /* legacy IO lock count */ 711d38fe6eSBjorn Helgaas unsigned int mem_lock_cnt; /* legacy MEM lock count */ 721d38fe6eSBjorn Helgaas unsigned int io_norm_cnt; /* normal IO count */ 731d38fe6eSBjorn Helgaas unsigned int mem_norm_cnt; /* normal MEM count */ 741d38fe6eSBjorn Helgaas bool bridge_has_one_vga; 751d38fe6eSBjorn Helgaas unsigned int (*set_decode)(struct pci_dev *pdev, bool decode); 761d38fe6eSBjorn Helgaas }; 771d38fe6eSBjorn Helgaas 781d38fe6eSBjorn Helgaas static LIST_HEAD(vga_list); 791d38fe6eSBjorn Helgaas static int vga_count, vga_decode_count; 801d38fe6eSBjorn Helgaas static bool vga_arbiter_used; 811d38fe6eSBjorn Helgaas static DEFINE_SPINLOCK(vga_lock); 821d38fe6eSBjorn Helgaas static DECLARE_WAIT_QUEUE_HEAD(vga_wait_queue); 831d38fe6eSBjorn Helgaas 841d38fe6eSBjorn Helgaas 851d38fe6eSBjorn Helgaas static const char *vga_iostate_to_str(unsigned int iostate) 861d38fe6eSBjorn Helgaas { 871d38fe6eSBjorn Helgaas /* Ignore VGA_RSRC_IO and VGA_RSRC_MEM */ 881d38fe6eSBjorn Helgaas iostate &= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; 891d38fe6eSBjorn Helgaas switch (iostate) { 901d38fe6eSBjorn Helgaas case VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM: 911d38fe6eSBjorn Helgaas return "io+mem"; 921d38fe6eSBjorn Helgaas case VGA_RSRC_LEGACY_IO: 931d38fe6eSBjorn Helgaas return "io"; 941d38fe6eSBjorn Helgaas case VGA_RSRC_LEGACY_MEM: 951d38fe6eSBjorn Helgaas return "mem"; 961d38fe6eSBjorn Helgaas } 971d38fe6eSBjorn Helgaas return "none"; 981d38fe6eSBjorn Helgaas } 991d38fe6eSBjorn Helgaas 1001d38fe6eSBjorn Helgaas static int vga_str_to_iostate(char *buf, int str_size, int *io_state) 1011d38fe6eSBjorn Helgaas { 1021d38fe6eSBjorn Helgaas /* we could in theory hand out locks on IO and mem 1031d38fe6eSBjorn Helgaas * separately to userspace but it can cause deadlocks */ 1041d38fe6eSBjorn Helgaas if (strncmp(buf, "none", 4) == 0) { 1051d38fe6eSBjorn Helgaas *io_state = VGA_RSRC_NONE; 1061d38fe6eSBjorn Helgaas return 1; 1071d38fe6eSBjorn Helgaas } 1081d38fe6eSBjorn Helgaas 1091d38fe6eSBjorn Helgaas /* XXX We're not chekcing the str_size! */ 1101d38fe6eSBjorn Helgaas if (strncmp(buf, "io+mem", 6) == 0) 1111d38fe6eSBjorn Helgaas goto both; 1121d38fe6eSBjorn Helgaas else if (strncmp(buf, "io", 2) == 0) 1131d38fe6eSBjorn Helgaas goto both; 1141d38fe6eSBjorn Helgaas else if (strncmp(buf, "mem", 3) == 0) 1151d38fe6eSBjorn Helgaas goto both; 1161d38fe6eSBjorn Helgaas return 0; 1171d38fe6eSBjorn Helgaas both: 1181d38fe6eSBjorn Helgaas *io_state = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; 1191d38fe6eSBjorn Helgaas return 1; 1201d38fe6eSBjorn Helgaas } 1211d38fe6eSBjorn Helgaas 1221d38fe6eSBjorn Helgaas /* this is only used a cookie - it should not be dereferenced */ 1231d38fe6eSBjorn Helgaas static struct pci_dev *vga_default; 1241d38fe6eSBjorn Helgaas 1251d38fe6eSBjorn Helgaas static void vga_arb_device_card_gone(struct pci_dev *pdev); 1261d38fe6eSBjorn Helgaas 1271d38fe6eSBjorn Helgaas /* Find somebody in our list */ 1281d38fe6eSBjorn Helgaas static struct vga_device *vgadev_find(struct pci_dev *pdev) 1291d38fe6eSBjorn Helgaas { 1301d38fe6eSBjorn Helgaas struct vga_device *vgadev; 1311d38fe6eSBjorn Helgaas 1321d38fe6eSBjorn Helgaas list_for_each_entry(vgadev, &vga_list, list) 1331d38fe6eSBjorn Helgaas if (pdev == vgadev->pdev) 1341d38fe6eSBjorn Helgaas return vgadev; 1351d38fe6eSBjorn Helgaas return NULL; 1361d38fe6eSBjorn Helgaas } 1371d38fe6eSBjorn Helgaas 1381d38fe6eSBjorn Helgaas /** 1391d38fe6eSBjorn Helgaas * vga_default_device - return the default VGA device, for vgacon 1401d38fe6eSBjorn Helgaas * 1411d38fe6eSBjorn Helgaas * This can be defined by the platform. The default implementation 1421d38fe6eSBjorn Helgaas * is rather dumb and will probably only work properly on single 1431d38fe6eSBjorn Helgaas * vga card setups and/or x86 platforms. 1441d38fe6eSBjorn Helgaas * 1451d38fe6eSBjorn Helgaas * If your VGA default device is not PCI, you'll have to return 1461d38fe6eSBjorn Helgaas * NULL here. In this case, I assume it will not conflict with 1471d38fe6eSBjorn Helgaas * any PCI card. If this is not true, I'll have to define two archs 1481d38fe6eSBjorn Helgaas * hooks for enabling/disabling the VGA default device if that is 1491d38fe6eSBjorn Helgaas * possible. This may be a problem with real _ISA_ VGA cards, in 1501d38fe6eSBjorn Helgaas * addition to a PCI one. I don't know at this point how to deal 1511d38fe6eSBjorn Helgaas * with that card. Can theirs IOs be disabled at all ? If not, then 1521d38fe6eSBjorn Helgaas * I suppose it's a matter of having the proper arch hook telling 1531d38fe6eSBjorn Helgaas * us about it, so we basically never allow anybody to succeed a 1541d38fe6eSBjorn Helgaas * vga_get()... 1551d38fe6eSBjorn Helgaas */ 1561d38fe6eSBjorn Helgaas struct pci_dev *vga_default_device(void) 1571d38fe6eSBjorn Helgaas { 1581d38fe6eSBjorn Helgaas return vga_default; 1591d38fe6eSBjorn Helgaas } 1601d38fe6eSBjorn Helgaas EXPORT_SYMBOL_GPL(vga_default_device); 1611d38fe6eSBjorn Helgaas 1621d38fe6eSBjorn Helgaas void vga_set_default_device(struct pci_dev *pdev) 1631d38fe6eSBjorn Helgaas { 1641d38fe6eSBjorn Helgaas if (vga_default == pdev) 1651d38fe6eSBjorn Helgaas return; 1661d38fe6eSBjorn Helgaas 1671d38fe6eSBjorn Helgaas pci_dev_put(vga_default); 1681d38fe6eSBjorn Helgaas vga_default = pci_dev_get(pdev); 1691d38fe6eSBjorn Helgaas } 1701d38fe6eSBjorn Helgaas 1711d38fe6eSBjorn Helgaas /** 1721d38fe6eSBjorn Helgaas * vga_remove_vgacon - deactivete vga console 1731d38fe6eSBjorn Helgaas * 1741d38fe6eSBjorn Helgaas * Unbind and unregister vgacon in case pdev is the default vga 1751d38fe6eSBjorn Helgaas * device. Can be called by gpu drivers on initialization to make 1761d38fe6eSBjorn Helgaas * sure vga register access done by vgacon will not disturb the 1771d38fe6eSBjorn Helgaas * device. 1781d38fe6eSBjorn Helgaas * 1791d38fe6eSBjorn Helgaas * @pdev: pci device. 1801d38fe6eSBjorn Helgaas */ 1811d38fe6eSBjorn Helgaas #if !defined(CONFIG_VGA_CONSOLE) 1821d38fe6eSBjorn Helgaas int vga_remove_vgacon(struct pci_dev *pdev) 1831d38fe6eSBjorn Helgaas { 1841d38fe6eSBjorn Helgaas return 0; 1851d38fe6eSBjorn Helgaas } 1861d38fe6eSBjorn Helgaas #elif !defined(CONFIG_DUMMY_CONSOLE) 1871d38fe6eSBjorn Helgaas int vga_remove_vgacon(struct pci_dev *pdev) 1881d38fe6eSBjorn Helgaas { 1891d38fe6eSBjorn Helgaas return -ENODEV; 1901d38fe6eSBjorn Helgaas } 1911d38fe6eSBjorn Helgaas #else 1921d38fe6eSBjorn Helgaas int vga_remove_vgacon(struct pci_dev *pdev) 1931d38fe6eSBjorn Helgaas { 1941d38fe6eSBjorn Helgaas int ret = 0; 1951d38fe6eSBjorn Helgaas 1961d38fe6eSBjorn Helgaas if (pdev != vga_default) 1971d38fe6eSBjorn Helgaas return 0; 1981d38fe6eSBjorn Helgaas vgaarb_info(&pdev->dev, "deactivate vga console\n"); 1991d38fe6eSBjorn Helgaas 2001d38fe6eSBjorn Helgaas console_lock(); 2011d38fe6eSBjorn Helgaas if (con_is_bound(&vga_con)) 2021d38fe6eSBjorn Helgaas ret = do_take_over_console(&dummy_con, 0, 2031d38fe6eSBjorn Helgaas MAX_NR_CONSOLES - 1, 1); 2041d38fe6eSBjorn Helgaas if (ret == 0) { 2051d38fe6eSBjorn Helgaas ret = do_unregister_con_driver(&vga_con); 2061d38fe6eSBjorn Helgaas 2071d38fe6eSBjorn Helgaas /* Ignore "already unregistered". */ 2081d38fe6eSBjorn Helgaas if (ret == -ENODEV) 2091d38fe6eSBjorn Helgaas ret = 0; 2101d38fe6eSBjorn Helgaas } 2111d38fe6eSBjorn Helgaas console_unlock(); 2121d38fe6eSBjorn Helgaas 2131d38fe6eSBjorn Helgaas return ret; 2141d38fe6eSBjorn Helgaas } 2151d38fe6eSBjorn Helgaas #endif 2161d38fe6eSBjorn Helgaas EXPORT_SYMBOL(vga_remove_vgacon); 2171d38fe6eSBjorn Helgaas 2181d38fe6eSBjorn Helgaas /* If we don't ever use VGA arb we should avoid 2191d38fe6eSBjorn Helgaas turning off anything anywhere due to old X servers getting 2201d38fe6eSBjorn Helgaas confused about the boot device not being VGA */ 2211d38fe6eSBjorn Helgaas static void vga_check_first_use(void) 2221d38fe6eSBjorn Helgaas { 2231d38fe6eSBjorn Helgaas /* we should inform all GPUs in the system that 2241d38fe6eSBjorn Helgaas * VGA arb has occurred and to try and disable resources 2251d38fe6eSBjorn Helgaas * if they can */ 2261d38fe6eSBjorn Helgaas if (!vga_arbiter_used) { 2271d38fe6eSBjorn Helgaas vga_arbiter_used = true; 2281d38fe6eSBjorn Helgaas vga_arbiter_notify_clients(); 2291d38fe6eSBjorn Helgaas } 2301d38fe6eSBjorn Helgaas } 2311d38fe6eSBjorn Helgaas 2321d38fe6eSBjorn Helgaas static struct vga_device *__vga_tryget(struct vga_device *vgadev, 2331d38fe6eSBjorn Helgaas unsigned int rsrc) 2341d38fe6eSBjorn Helgaas { 2351d38fe6eSBjorn Helgaas struct device *dev = &vgadev->pdev->dev; 2361d38fe6eSBjorn Helgaas unsigned int wants, legacy_wants, match; 2371d38fe6eSBjorn Helgaas struct vga_device *conflict; 2381d38fe6eSBjorn Helgaas unsigned int pci_bits; 2391d38fe6eSBjorn Helgaas u32 flags = 0; 2401d38fe6eSBjorn Helgaas 2411d38fe6eSBjorn Helgaas /* Account for "normal" resources to lock. If we decode the legacy, 2421d38fe6eSBjorn Helgaas * counterpart, we need to request it as well 2431d38fe6eSBjorn Helgaas */ 2441d38fe6eSBjorn Helgaas if ((rsrc & VGA_RSRC_NORMAL_IO) && 2451d38fe6eSBjorn Helgaas (vgadev->decodes & VGA_RSRC_LEGACY_IO)) 2461d38fe6eSBjorn Helgaas rsrc |= VGA_RSRC_LEGACY_IO; 2471d38fe6eSBjorn Helgaas if ((rsrc & VGA_RSRC_NORMAL_MEM) && 2481d38fe6eSBjorn Helgaas (vgadev->decodes & VGA_RSRC_LEGACY_MEM)) 2491d38fe6eSBjorn Helgaas rsrc |= VGA_RSRC_LEGACY_MEM; 2501d38fe6eSBjorn Helgaas 2511d38fe6eSBjorn Helgaas vgaarb_dbg(dev, "%s: %d\n", __func__, rsrc); 2521d38fe6eSBjorn Helgaas vgaarb_dbg(dev, "%s: owns: %d\n", __func__, vgadev->owns); 2531d38fe6eSBjorn Helgaas 2541d38fe6eSBjorn Helgaas /* Check what resources we need to acquire */ 2551d38fe6eSBjorn Helgaas wants = rsrc & ~vgadev->owns; 2561d38fe6eSBjorn Helgaas 2571d38fe6eSBjorn Helgaas /* We already own everything, just mark locked & bye bye */ 2581d38fe6eSBjorn Helgaas if (wants == 0) 2591d38fe6eSBjorn Helgaas goto lock_them; 2601d38fe6eSBjorn Helgaas 2611d38fe6eSBjorn Helgaas /* We don't need to request a legacy resource, we just enable 2621d38fe6eSBjorn Helgaas * appropriate decoding and go 2631d38fe6eSBjorn Helgaas */ 2641d38fe6eSBjorn Helgaas legacy_wants = wants & VGA_RSRC_LEGACY_MASK; 2651d38fe6eSBjorn Helgaas if (legacy_wants == 0) 2661d38fe6eSBjorn Helgaas goto enable_them; 2671d38fe6eSBjorn Helgaas 2681d38fe6eSBjorn Helgaas /* Ok, we don't, let's find out how we need to kick off */ 2691d38fe6eSBjorn Helgaas list_for_each_entry(conflict, &vga_list, list) { 2701d38fe6eSBjorn Helgaas unsigned int lwants = legacy_wants; 2711d38fe6eSBjorn Helgaas unsigned int change_bridge = 0; 2721d38fe6eSBjorn Helgaas 2731d38fe6eSBjorn Helgaas /* Don't conflict with myself */ 2741d38fe6eSBjorn Helgaas if (vgadev == conflict) 2751d38fe6eSBjorn Helgaas continue; 2761d38fe6eSBjorn Helgaas 2771d38fe6eSBjorn Helgaas /* We have a possible conflict. before we go further, we must 2781d38fe6eSBjorn Helgaas * check if we sit on the same bus as the conflicting device. 2791d38fe6eSBjorn Helgaas * if we don't, then we must tie both IO and MEM resources 2801d38fe6eSBjorn Helgaas * together since there is only a single bit controlling 2811d38fe6eSBjorn Helgaas * VGA forwarding on P2P bridges 2821d38fe6eSBjorn Helgaas */ 2831d38fe6eSBjorn Helgaas if (vgadev->pdev->bus != conflict->pdev->bus) { 2841d38fe6eSBjorn Helgaas change_bridge = 1; 2851d38fe6eSBjorn Helgaas lwants = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; 2861d38fe6eSBjorn Helgaas } 2871d38fe6eSBjorn Helgaas 2881d38fe6eSBjorn Helgaas /* Check if the guy has a lock on the resource. If he does, 2891d38fe6eSBjorn Helgaas * return the conflicting entry 2901d38fe6eSBjorn Helgaas */ 2911d38fe6eSBjorn Helgaas if (conflict->locks & lwants) 2921d38fe6eSBjorn Helgaas return conflict; 2931d38fe6eSBjorn Helgaas 2941d38fe6eSBjorn Helgaas /* Ok, now check if it owns the resource we want. We can 2951d38fe6eSBjorn Helgaas * lock resources that are not decoded, therefore a device 2961d38fe6eSBjorn Helgaas * can own resources it doesn't decode. 2971d38fe6eSBjorn Helgaas */ 2981d38fe6eSBjorn Helgaas match = lwants & conflict->owns; 2991d38fe6eSBjorn Helgaas if (!match) 3001d38fe6eSBjorn Helgaas continue; 3011d38fe6eSBjorn Helgaas 3021d38fe6eSBjorn Helgaas /* looks like he doesn't have a lock, we can steal 3031d38fe6eSBjorn Helgaas * them from him 3041d38fe6eSBjorn Helgaas */ 3051d38fe6eSBjorn Helgaas 3061d38fe6eSBjorn Helgaas flags = 0; 3071d38fe6eSBjorn Helgaas pci_bits = 0; 3081d38fe6eSBjorn Helgaas 3091d38fe6eSBjorn Helgaas /* If we can't control legacy resources via the bridge, we 3101d38fe6eSBjorn Helgaas * also need to disable normal decoding. 3111d38fe6eSBjorn Helgaas */ 3121d38fe6eSBjorn Helgaas if (!conflict->bridge_has_one_vga) { 3131d38fe6eSBjorn Helgaas if ((match & conflict->decodes) & VGA_RSRC_LEGACY_MEM) 3141d38fe6eSBjorn Helgaas pci_bits |= PCI_COMMAND_MEMORY; 3151d38fe6eSBjorn Helgaas if ((match & conflict->decodes) & VGA_RSRC_LEGACY_IO) 3161d38fe6eSBjorn Helgaas pci_bits |= PCI_COMMAND_IO; 3171d38fe6eSBjorn Helgaas 3181d38fe6eSBjorn Helgaas if (pci_bits) 3191d38fe6eSBjorn Helgaas flags |= PCI_VGA_STATE_CHANGE_DECODES; 3201d38fe6eSBjorn Helgaas } 3211d38fe6eSBjorn Helgaas 3221d38fe6eSBjorn Helgaas if (change_bridge) 3231d38fe6eSBjorn Helgaas flags |= PCI_VGA_STATE_CHANGE_BRIDGE; 3241d38fe6eSBjorn Helgaas 3251d38fe6eSBjorn Helgaas pci_set_vga_state(conflict->pdev, false, pci_bits, flags); 3261d38fe6eSBjorn Helgaas conflict->owns &= ~match; 3271d38fe6eSBjorn Helgaas 3281d38fe6eSBjorn Helgaas /* If we disabled normal decoding, reflect it in owns */ 3291d38fe6eSBjorn Helgaas if (pci_bits & PCI_COMMAND_MEMORY) 3301d38fe6eSBjorn Helgaas conflict->owns &= ~VGA_RSRC_NORMAL_MEM; 3311d38fe6eSBjorn Helgaas if (pci_bits & PCI_COMMAND_IO) 3321d38fe6eSBjorn Helgaas conflict->owns &= ~VGA_RSRC_NORMAL_IO; 3331d38fe6eSBjorn Helgaas } 3341d38fe6eSBjorn Helgaas 3351d38fe6eSBjorn Helgaas enable_them: 3361d38fe6eSBjorn Helgaas /* ok dude, we got it, everybody conflicting has been disabled, let's 3371d38fe6eSBjorn Helgaas * enable us. Mark any bits in "owns" regardless of whether we 3381d38fe6eSBjorn Helgaas * decoded them. We can lock resources we don't decode, therefore 3391d38fe6eSBjorn Helgaas * we must track them via "owns". 3401d38fe6eSBjorn Helgaas */ 3411d38fe6eSBjorn Helgaas flags = 0; 3421d38fe6eSBjorn Helgaas pci_bits = 0; 3431d38fe6eSBjorn Helgaas 3441d38fe6eSBjorn Helgaas if (!vgadev->bridge_has_one_vga) { 3451d38fe6eSBjorn Helgaas flags |= PCI_VGA_STATE_CHANGE_DECODES; 3461d38fe6eSBjorn Helgaas if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) 3471d38fe6eSBjorn Helgaas pci_bits |= PCI_COMMAND_MEMORY; 3481d38fe6eSBjorn Helgaas if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) 3491d38fe6eSBjorn Helgaas pci_bits |= PCI_COMMAND_IO; 3501d38fe6eSBjorn Helgaas } 3511d38fe6eSBjorn Helgaas if (wants & VGA_RSRC_LEGACY_MASK) 3521d38fe6eSBjorn Helgaas flags |= PCI_VGA_STATE_CHANGE_BRIDGE; 3531d38fe6eSBjorn Helgaas 3541d38fe6eSBjorn Helgaas pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); 3551d38fe6eSBjorn Helgaas 3561d38fe6eSBjorn Helgaas vgadev->owns |= wants; 3571d38fe6eSBjorn Helgaas lock_them: 3581d38fe6eSBjorn Helgaas vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK); 3591d38fe6eSBjorn Helgaas if (rsrc & VGA_RSRC_LEGACY_IO) 3601d38fe6eSBjorn Helgaas vgadev->io_lock_cnt++; 3611d38fe6eSBjorn Helgaas if (rsrc & VGA_RSRC_LEGACY_MEM) 3621d38fe6eSBjorn Helgaas vgadev->mem_lock_cnt++; 3631d38fe6eSBjorn Helgaas if (rsrc & VGA_RSRC_NORMAL_IO) 3641d38fe6eSBjorn Helgaas vgadev->io_norm_cnt++; 3651d38fe6eSBjorn Helgaas if (rsrc & VGA_RSRC_NORMAL_MEM) 3661d38fe6eSBjorn Helgaas vgadev->mem_norm_cnt++; 3671d38fe6eSBjorn Helgaas 3681d38fe6eSBjorn Helgaas return NULL; 3691d38fe6eSBjorn Helgaas } 3701d38fe6eSBjorn Helgaas 3711d38fe6eSBjorn Helgaas static void __vga_put(struct vga_device *vgadev, unsigned int rsrc) 3721d38fe6eSBjorn Helgaas { 3731d38fe6eSBjorn Helgaas struct device *dev = &vgadev->pdev->dev; 3741d38fe6eSBjorn Helgaas unsigned int old_locks = vgadev->locks; 3751d38fe6eSBjorn Helgaas 3761d38fe6eSBjorn Helgaas vgaarb_dbg(dev, "%s\n", __func__); 3771d38fe6eSBjorn Helgaas 3781d38fe6eSBjorn Helgaas /* Update our counters, and account for equivalent legacy resources 3791d38fe6eSBjorn Helgaas * if we decode them 3801d38fe6eSBjorn Helgaas */ 3811d38fe6eSBjorn Helgaas if ((rsrc & VGA_RSRC_NORMAL_IO) && vgadev->io_norm_cnt > 0) { 3821d38fe6eSBjorn Helgaas vgadev->io_norm_cnt--; 3831d38fe6eSBjorn Helgaas if (vgadev->decodes & VGA_RSRC_LEGACY_IO) 3841d38fe6eSBjorn Helgaas rsrc |= VGA_RSRC_LEGACY_IO; 3851d38fe6eSBjorn Helgaas } 3861d38fe6eSBjorn Helgaas if ((rsrc & VGA_RSRC_NORMAL_MEM) && vgadev->mem_norm_cnt > 0) { 3871d38fe6eSBjorn Helgaas vgadev->mem_norm_cnt--; 3881d38fe6eSBjorn Helgaas if (vgadev->decodes & VGA_RSRC_LEGACY_MEM) 3891d38fe6eSBjorn Helgaas rsrc |= VGA_RSRC_LEGACY_MEM; 3901d38fe6eSBjorn Helgaas } 3911d38fe6eSBjorn Helgaas if ((rsrc & VGA_RSRC_LEGACY_IO) && vgadev->io_lock_cnt > 0) 3921d38fe6eSBjorn Helgaas vgadev->io_lock_cnt--; 3931d38fe6eSBjorn Helgaas if ((rsrc & VGA_RSRC_LEGACY_MEM) && vgadev->mem_lock_cnt > 0) 3941d38fe6eSBjorn Helgaas vgadev->mem_lock_cnt--; 3951d38fe6eSBjorn Helgaas 3961d38fe6eSBjorn Helgaas /* Just clear lock bits, we do lazy operations so we don't really 3971d38fe6eSBjorn Helgaas * have to bother about anything else at this point 3981d38fe6eSBjorn Helgaas */ 3991d38fe6eSBjorn Helgaas if (vgadev->io_lock_cnt == 0) 4001d38fe6eSBjorn Helgaas vgadev->locks &= ~VGA_RSRC_LEGACY_IO; 4011d38fe6eSBjorn Helgaas if (vgadev->mem_lock_cnt == 0) 4021d38fe6eSBjorn Helgaas vgadev->locks &= ~VGA_RSRC_LEGACY_MEM; 4031d38fe6eSBjorn Helgaas 4041d38fe6eSBjorn Helgaas /* Kick the wait queue in case somebody was waiting if we actually 4051d38fe6eSBjorn Helgaas * released something 4061d38fe6eSBjorn Helgaas */ 4071d38fe6eSBjorn Helgaas if (old_locks != vgadev->locks) 4081d38fe6eSBjorn Helgaas wake_up_all(&vga_wait_queue); 4091d38fe6eSBjorn Helgaas } 4101d38fe6eSBjorn Helgaas 4111d38fe6eSBjorn Helgaas /** 4121d38fe6eSBjorn Helgaas * vga_get - acquire & locks VGA resources 4131d38fe6eSBjorn Helgaas * @pdev: pci device of the VGA card or NULL for the system default 4141d38fe6eSBjorn Helgaas * @rsrc: bit mask of resources to acquire and lock 4151d38fe6eSBjorn Helgaas * @interruptible: blocking should be interruptible by signals ? 4161d38fe6eSBjorn Helgaas * 4171d38fe6eSBjorn Helgaas * This function acquires VGA resources for the given card and mark those 4181d38fe6eSBjorn Helgaas * resources locked. If the resource requested are "normal" (and not legacy) 4191d38fe6eSBjorn Helgaas * resources, the arbiter will first check whether the card is doing legacy 4201d38fe6eSBjorn Helgaas * decoding for that type of resource. If yes, the lock is "converted" into a 4211d38fe6eSBjorn Helgaas * legacy resource lock. 4221d38fe6eSBjorn Helgaas * 4231d38fe6eSBjorn Helgaas * The arbiter will first look for all VGA cards that might conflict and disable 4241d38fe6eSBjorn Helgaas * their IOs and/or Memory access, including VGA forwarding on P2P bridges if 4251d38fe6eSBjorn Helgaas * necessary, so that the requested resources can be used. Then, the card is 4261d38fe6eSBjorn Helgaas * marked as locking these resources and the IO and/or Memory accesses are 4271d38fe6eSBjorn Helgaas * enabled on the card (including VGA forwarding on parent P2P bridges if any). 4281d38fe6eSBjorn Helgaas * 4291d38fe6eSBjorn Helgaas * This function will block if some conflicting card is already locking one of 4301d38fe6eSBjorn Helgaas * the required resources (or any resource on a different bus segment, since P2P 4311d38fe6eSBjorn Helgaas * bridges don't differentiate VGA memory and IO afaik). You can indicate 4321d38fe6eSBjorn Helgaas * whether this blocking should be interruptible by a signal (for userland 4331d38fe6eSBjorn Helgaas * interface) or not. 4341d38fe6eSBjorn Helgaas * 4351d38fe6eSBjorn Helgaas * Must not be called at interrupt time or in atomic context. If the card 4361d38fe6eSBjorn Helgaas * already owns the resources, the function succeeds. Nested calls are 4371d38fe6eSBjorn Helgaas * supported (a per-resource counter is maintained) 4381d38fe6eSBjorn Helgaas * 4391d38fe6eSBjorn Helgaas * On success, release the VGA resource again with vga_put(). 4401d38fe6eSBjorn Helgaas * 4411d38fe6eSBjorn Helgaas * Returns: 4421d38fe6eSBjorn Helgaas * 4431d38fe6eSBjorn Helgaas * 0 on success, negative error code on failure. 4441d38fe6eSBjorn Helgaas */ 4451d38fe6eSBjorn Helgaas int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible) 4461d38fe6eSBjorn Helgaas { 4471d38fe6eSBjorn Helgaas struct vga_device *vgadev, *conflict; 4481d38fe6eSBjorn Helgaas unsigned long flags; 4491d38fe6eSBjorn Helgaas wait_queue_entry_t wait; 4501d38fe6eSBjorn Helgaas int rc = 0; 4511d38fe6eSBjorn Helgaas 4521d38fe6eSBjorn Helgaas vga_check_first_use(); 4531d38fe6eSBjorn Helgaas /* The one who calls us should check for this, but lets be sure... */ 4541d38fe6eSBjorn Helgaas if (pdev == NULL) 4551d38fe6eSBjorn Helgaas pdev = vga_default_device(); 4561d38fe6eSBjorn Helgaas if (pdev == NULL) 4571d38fe6eSBjorn Helgaas return 0; 4581d38fe6eSBjorn Helgaas 4591d38fe6eSBjorn Helgaas for (;;) { 4601d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 4611d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 4621d38fe6eSBjorn Helgaas if (vgadev == NULL) { 4631d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 4641d38fe6eSBjorn Helgaas rc = -ENODEV; 4651d38fe6eSBjorn Helgaas break; 4661d38fe6eSBjorn Helgaas } 4671d38fe6eSBjorn Helgaas conflict = __vga_tryget(vgadev, rsrc); 4681d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 4691d38fe6eSBjorn Helgaas if (conflict == NULL) 4701d38fe6eSBjorn Helgaas break; 4711d38fe6eSBjorn Helgaas 4721d38fe6eSBjorn Helgaas 4731d38fe6eSBjorn Helgaas /* We have a conflict, we wait until somebody kicks the 4741d38fe6eSBjorn Helgaas * work queue. Currently we have one work queue that we 4751d38fe6eSBjorn Helgaas * kick each time some resources are released, but it would 4761d38fe6eSBjorn Helgaas * be fairly easy to have a per device one so that we only 4771d38fe6eSBjorn Helgaas * need to attach to the conflicting device 4781d38fe6eSBjorn Helgaas */ 4791d38fe6eSBjorn Helgaas init_waitqueue_entry(&wait, current); 4801d38fe6eSBjorn Helgaas add_wait_queue(&vga_wait_queue, &wait); 4811d38fe6eSBjorn Helgaas set_current_state(interruptible ? 4821d38fe6eSBjorn Helgaas TASK_INTERRUPTIBLE : 4831d38fe6eSBjorn Helgaas TASK_UNINTERRUPTIBLE); 4841d38fe6eSBjorn Helgaas if (interruptible && signal_pending(current)) { 4851d38fe6eSBjorn Helgaas __set_current_state(TASK_RUNNING); 4861d38fe6eSBjorn Helgaas remove_wait_queue(&vga_wait_queue, &wait); 4871d38fe6eSBjorn Helgaas rc = -ERESTARTSYS; 4881d38fe6eSBjorn Helgaas break; 4891d38fe6eSBjorn Helgaas } 4901d38fe6eSBjorn Helgaas schedule(); 4911d38fe6eSBjorn Helgaas remove_wait_queue(&vga_wait_queue, &wait); 4921d38fe6eSBjorn Helgaas } 4931d38fe6eSBjorn Helgaas return rc; 4941d38fe6eSBjorn Helgaas } 4951d38fe6eSBjorn Helgaas EXPORT_SYMBOL(vga_get); 4961d38fe6eSBjorn Helgaas 4971d38fe6eSBjorn Helgaas /** 4981d38fe6eSBjorn Helgaas * vga_tryget - try to acquire & lock legacy VGA resources 4991d38fe6eSBjorn Helgaas * @pdev: pci devivce of VGA card or NULL for system default 5001d38fe6eSBjorn Helgaas * @rsrc: bit mask of resources to acquire and lock 5011d38fe6eSBjorn Helgaas * 5021d38fe6eSBjorn Helgaas * This function performs the same operation as vga_get(), but will return an 5031d38fe6eSBjorn Helgaas * error (-EBUSY) instead of blocking if the resources are already locked by 5041d38fe6eSBjorn Helgaas * another card. It can be called in any context 5051d38fe6eSBjorn Helgaas * 5061d38fe6eSBjorn Helgaas * On success, release the VGA resource again with vga_put(). 5071d38fe6eSBjorn Helgaas * 5081d38fe6eSBjorn Helgaas * Returns: 5091d38fe6eSBjorn Helgaas * 5101d38fe6eSBjorn Helgaas * 0 on success, negative error code on failure. 5111d38fe6eSBjorn Helgaas */ 5121d38fe6eSBjorn Helgaas static int vga_tryget(struct pci_dev *pdev, unsigned int rsrc) 5131d38fe6eSBjorn Helgaas { 5141d38fe6eSBjorn Helgaas struct vga_device *vgadev; 5151d38fe6eSBjorn Helgaas unsigned long flags; 5161d38fe6eSBjorn Helgaas int rc = 0; 5171d38fe6eSBjorn Helgaas 5181d38fe6eSBjorn Helgaas vga_check_first_use(); 5191d38fe6eSBjorn Helgaas 5201d38fe6eSBjorn Helgaas /* The one who calls us should check for this, but lets be sure... */ 5211d38fe6eSBjorn Helgaas if (pdev == NULL) 5221d38fe6eSBjorn Helgaas pdev = vga_default_device(); 5231d38fe6eSBjorn Helgaas if (pdev == NULL) 5241d38fe6eSBjorn Helgaas return 0; 5251d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 5261d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 5271d38fe6eSBjorn Helgaas if (vgadev == NULL) { 5281d38fe6eSBjorn Helgaas rc = -ENODEV; 5291d38fe6eSBjorn Helgaas goto bail; 5301d38fe6eSBjorn Helgaas } 5311d38fe6eSBjorn Helgaas if (__vga_tryget(vgadev, rsrc)) 5321d38fe6eSBjorn Helgaas rc = -EBUSY; 5331d38fe6eSBjorn Helgaas bail: 5341d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 5351d38fe6eSBjorn Helgaas return rc; 5361d38fe6eSBjorn Helgaas } 5371d38fe6eSBjorn Helgaas 5381d38fe6eSBjorn Helgaas /** 5391d38fe6eSBjorn Helgaas * vga_put - release lock on legacy VGA resources 5401d38fe6eSBjorn Helgaas * @pdev: pci device of VGA card or NULL for system default 5411d38fe6eSBjorn Helgaas * @rsrc: but mask of resource to release 5421d38fe6eSBjorn Helgaas * 5431d38fe6eSBjorn Helgaas * This fuction releases resources previously locked by vga_get() or 5441d38fe6eSBjorn Helgaas * vga_tryget(). The resources aren't disabled right away, so that a subsequence 5451d38fe6eSBjorn Helgaas * vga_get() on the same card will succeed immediately. Resources have a 5461d38fe6eSBjorn Helgaas * counter, so locks are only released if the counter reaches 0. 5471d38fe6eSBjorn Helgaas */ 5481d38fe6eSBjorn Helgaas void vga_put(struct pci_dev *pdev, unsigned int rsrc) 5491d38fe6eSBjorn Helgaas { 5501d38fe6eSBjorn Helgaas struct vga_device *vgadev; 5511d38fe6eSBjorn Helgaas unsigned long flags; 5521d38fe6eSBjorn Helgaas 5531d38fe6eSBjorn Helgaas /* The one who calls us should check for this, but lets be sure... */ 5541d38fe6eSBjorn Helgaas if (pdev == NULL) 5551d38fe6eSBjorn Helgaas pdev = vga_default_device(); 5561d38fe6eSBjorn Helgaas if (pdev == NULL) 5571d38fe6eSBjorn Helgaas return; 5581d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 5591d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 5601d38fe6eSBjorn Helgaas if (vgadev == NULL) 5611d38fe6eSBjorn Helgaas goto bail; 5621d38fe6eSBjorn Helgaas __vga_put(vgadev, rsrc); 5631d38fe6eSBjorn Helgaas bail: 5641d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 5651d38fe6eSBjorn Helgaas } 5661d38fe6eSBjorn Helgaas EXPORT_SYMBOL(vga_put); 5671d38fe6eSBjorn Helgaas 568*60a9bac8SBjorn Helgaas static void __init vga_select_framebuffer_device(struct pci_dev *pdev) 569*60a9bac8SBjorn Helgaas { 570*60a9bac8SBjorn Helgaas #if defined(CONFIG_X86) || defined(CONFIG_IA64) 571*60a9bac8SBjorn Helgaas struct device *dev = &pdev->dev; 572*60a9bac8SBjorn Helgaas u64 base = screen_info.lfb_base; 573*60a9bac8SBjorn Helgaas u64 size = screen_info.lfb_size; 574*60a9bac8SBjorn Helgaas u64 limit; 575*60a9bac8SBjorn Helgaas resource_size_t start, end; 576*60a9bac8SBjorn Helgaas unsigned long flags; 577*60a9bac8SBjorn Helgaas int i; 578*60a9bac8SBjorn Helgaas 579*60a9bac8SBjorn Helgaas /* Select the device owning the boot framebuffer if there is one */ 580*60a9bac8SBjorn Helgaas 581*60a9bac8SBjorn Helgaas if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) 582*60a9bac8SBjorn Helgaas base |= (u64)screen_info.ext_lfb_base << 32; 583*60a9bac8SBjorn Helgaas 584*60a9bac8SBjorn Helgaas limit = base + size; 585*60a9bac8SBjorn Helgaas 586*60a9bac8SBjorn Helgaas /* 587*60a9bac8SBjorn Helgaas * Override vga_arbiter_add_pci_device()'s I/O based detection 588*60a9bac8SBjorn Helgaas * as it may take the wrong device (e.g. on Apple system under 589*60a9bac8SBjorn Helgaas * EFI). 590*60a9bac8SBjorn Helgaas * 591*60a9bac8SBjorn Helgaas * Select the device owning the boot framebuffer if there is 592*60a9bac8SBjorn Helgaas * one. 593*60a9bac8SBjorn Helgaas */ 594*60a9bac8SBjorn Helgaas 595*60a9bac8SBjorn Helgaas /* Does firmware framebuffer belong to us? */ 596*60a9bac8SBjorn Helgaas for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 597*60a9bac8SBjorn Helgaas flags = pci_resource_flags(pdev, i); 598*60a9bac8SBjorn Helgaas 599*60a9bac8SBjorn Helgaas if ((flags & IORESOURCE_MEM) == 0) 600*60a9bac8SBjorn Helgaas continue; 601*60a9bac8SBjorn Helgaas 602*60a9bac8SBjorn Helgaas start = pci_resource_start(pdev, i); 603*60a9bac8SBjorn Helgaas end = pci_resource_end(pdev, i); 604*60a9bac8SBjorn Helgaas 605*60a9bac8SBjorn Helgaas if (!start || !end) 606*60a9bac8SBjorn Helgaas continue; 607*60a9bac8SBjorn Helgaas 608*60a9bac8SBjorn Helgaas if (base < start || limit >= end) 609*60a9bac8SBjorn Helgaas continue; 610*60a9bac8SBjorn Helgaas 611*60a9bac8SBjorn Helgaas if (!vga_default_device()) 612*60a9bac8SBjorn Helgaas vgaarb_info(dev, "setting as boot device\n"); 613*60a9bac8SBjorn Helgaas else if (pdev != vga_default_device()) 614*60a9bac8SBjorn Helgaas vgaarb_info(dev, "overriding boot device\n"); 615*60a9bac8SBjorn Helgaas vga_set_default_device(pdev); 616*60a9bac8SBjorn Helgaas } 617*60a9bac8SBjorn Helgaas #endif 618*60a9bac8SBjorn Helgaas } 619*60a9bac8SBjorn Helgaas 620c1593dddSHuacai Chen static bool vga_arb_integrated_gpu(struct device *dev) 621c1593dddSHuacai Chen { 622c1593dddSHuacai Chen #if defined(CONFIG_ACPI) 623c1593dddSHuacai Chen struct acpi_device *adev = ACPI_COMPANION(dev); 624c1593dddSHuacai Chen 625c1593dddSHuacai Chen return adev && !strcmp(acpi_device_hid(adev), ACPI_VIDEO_HID); 626c1593dddSHuacai Chen #else 627c1593dddSHuacai Chen return false; 628c1593dddSHuacai Chen #endif 629c1593dddSHuacai Chen } 630c1593dddSHuacai Chen 6311d38fe6eSBjorn Helgaas /* 6321d38fe6eSBjorn Helgaas * Rules for using a bridge to control a VGA descendant decoding: if a bridge 6331d38fe6eSBjorn Helgaas * has only one VGA descendant then it can be used to control the VGA routing 6341d38fe6eSBjorn Helgaas * for that device. It should always use the bridge closest to the device to 6351d38fe6eSBjorn Helgaas * control it. If a bridge has a direct VGA descendant, but also have a sub- 6361d38fe6eSBjorn Helgaas * bridge VGA descendant then we cannot use that bridge to control the direct 6371d38fe6eSBjorn Helgaas * VGA descendant. So for every device we register, we need to iterate all 6381d38fe6eSBjorn Helgaas * its parent bridges so we can invalidate any devices using them properly. 6391d38fe6eSBjorn Helgaas */ 6401d38fe6eSBjorn Helgaas static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev) 6411d38fe6eSBjorn Helgaas { 6421d38fe6eSBjorn Helgaas struct vga_device *same_bridge_vgadev; 6431d38fe6eSBjorn Helgaas struct pci_bus *new_bus, *bus; 6441d38fe6eSBjorn Helgaas struct pci_dev *new_bridge, *bridge; 6451d38fe6eSBjorn Helgaas 6461d38fe6eSBjorn Helgaas vgadev->bridge_has_one_vga = true; 6471d38fe6eSBjorn Helgaas 6481d38fe6eSBjorn Helgaas if (list_empty(&vga_list)) 6491d38fe6eSBjorn Helgaas return; 6501d38fe6eSBjorn Helgaas 6511d38fe6eSBjorn Helgaas /* okay iterate the new devices bridge hierarachy */ 6521d38fe6eSBjorn Helgaas new_bus = vgadev->pdev->bus; 6531d38fe6eSBjorn Helgaas while (new_bus) { 6541d38fe6eSBjorn Helgaas new_bridge = new_bus->self; 6551d38fe6eSBjorn Helgaas 6561d38fe6eSBjorn Helgaas /* go through list of devices already registered */ 6571d38fe6eSBjorn Helgaas list_for_each_entry(same_bridge_vgadev, &vga_list, list) { 6581d38fe6eSBjorn Helgaas bus = same_bridge_vgadev->pdev->bus; 6591d38fe6eSBjorn Helgaas bridge = bus->self; 6601d38fe6eSBjorn Helgaas 6611d38fe6eSBjorn Helgaas /* see if the share a bridge with this device */ 6621d38fe6eSBjorn Helgaas if (new_bridge == bridge) { 6631d38fe6eSBjorn Helgaas /* 6641d38fe6eSBjorn Helgaas * If their direct parent bridge is the same 6651d38fe6eSBjorn Helgaas * as any bridge of this device then it can't 6661d38fe6eSBjorn Helgaas * be used for that device. 6671d38fe6eSBjorn Helgaas */ 6681d38fe6eSBjorn Helgaas same_bridge_vgadev->bridge_has_one_vga = false; 6691d38fe6eSBjorn Helgaas } 6701d38fe6eSBjorn Helgaas 6711d38fe6eSBjorn Helgaas /* 6721d38fe6eSBjorn Helgaas * Now iterate the previous devices bridge hierarchy. 6731d38fe6eSBjorn Helgaas * If the new devices parent bridge is in the other 6741d38fe6eSBjorn Helgaas * devices hierarchy then we can't use it to control 6751d38fe6eSBjorn Helgaas * this device 6761d38fe6eSBjorn Helgaas */ 6771d38fe6eSBjorn Helgaas while (bus) { 6781d38fe6eSBjorn Helgaas bridge = bus->self; 6791d38fe6eSBjorn Helgaas 6801d38fe6eSBjorn Helgaas if (bridge && bridge == vgadev->pdev->bus->self) 6811d38fe6eSBjorn Helgaas vgadev->bridge_has_one_vga = false; 6821d38fe6eSBjorn Helgaas 6831d38fe6eSBjorn Helgaas bus = bus->parent; 6841d38fe6eSBjorn Helgaas } 6851d38fe6eSBjorn Helgaas } 6861d38fe6eSBjorn Helgaas new_bus = new_bus->parent; 6871d38fe6eSBjorn Helgaas } 6881d38fe6eSBjorn Helgaas } 6891d38fe6eSBjorn Helgaas 6901d38fe6eSBjorn Helgaas /* 6911d38fe6eSBjorn Helgaas * Currently, we assume that the "initial" setup of the system is 6921d38fe6eSBjorn Helgaas * not sane, that is we come up with conflicting devices and let 6931d38fe6eSBjorn Helgaas * the arbiter's client decides if devices decodes or not legacy 6941d38fe6eSBjorn Helgaas * things. 6951d38fe6eSBjorn Helgaas */ 6961d38fe6eSBjorn Helgaas static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) 6971d38fe6eSBjorn Helgaas { 6981d38fe6eSBjorn Helgaas struct vga_device *vgadev; 6991d38fe6eSBjorn Helgaas unsigned long flags; 7001d38fe6eSBjorn Helgaas struct pci_bus *bus; 7011d38fe6eSBjorn Helgaas struct pci_dev *bridge; 7021d38fe6eSBjorn Helgaas u16 cmd; 7031d38fe6eSBjorn Helgaas 7041d38fe6eSBjorn Helgaas /* Only deal with VGA class devices */ 7051d38fe6eSBjorn Helgaas if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) 7061d38fe6eSBjorn Helgaas return false; 7071d38fe6eSBjorn Helgaas 7081d38fe6eSBjorn Helgaas /* Allocate structure */ 7091d38fe6eSBjorn Helgaas vgadev = kzalloc(sizeof(struct vga_device), GFP_KERNEL); 7101d38fe6eSBjorn Helgaas if (vgadev == NULL) { 7111d38fe6eSBjorn Helgaas vgaarb_err(&pdev->dev, "failed to allocate VGA arbiter data\n"); 7121d38fe6eSBjorn Helgaas /* 7131d38fe6eSBjorn Helgaas * What to do on allocation failure ? For now, let's just do 7141d38fe6eSBjorn Helgaas * nothing, I'm not sure there is anything saner to be done. 7151d38fe6eSBjorn Helgaas */ 7161d38fe6eSBjorn Helgaas return false; 7171d38fe6eSBjorn Helgaas } 7181d38fe6eSBjorn Helgaas 7191d38fe6eSBjorn Helgaas /* Take lock & check for duplicates */ 7201d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 7211d38fe6eSBjorn Helgaas if (vgadev_find(pdev) != NULL) { 7221d38fe6eSBjorn Helgaas BUG_ON(1); 7231d38fe6eSBjorn Helgaas goto fail; 7241d38fe6eSBjorn Helgaas } 7251d38fe6eSBjorn Helgaas vgadev->pdev = pdev; 7261d38fe6eSBjorn Helgaas 7271d38fe6eSBjorn Helgaas /* By default, assume we decode everything */ 7281d38fe6eSBjorn Helgaas vgadev->decodes = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | 7291d38fe6eSBjorn Helgaas VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 7301d38fe6eSBjorn Helgaas 7311d38fe6eSBjorn Helgaas /* by default mark it as decoding */ 7321d38fe6eSBjorn Helgaas vga_decode_count++; 7331d38fe6eSBjorn Helgaas /* Mark that we "own" resources based on our enables, we will 7341d38fe6eSBjorn Helgaas * clear that below if the bridge isn't forwarding 7351d38fe6eSBjorn Helgaas */ 7361d38fe6eSBjorn Helgaas pci_read_config_word(pdev, PCI_COMMAND, &cmd); 7371d38fe6eSBjorn Helgaas if (cmd & PCI_COMMAND_IO) 7381d38fe6eSBjorn Helgaas vgadev->owns |= VGA_RSRC_LEGACY_IO; 7391d38fe6eSBjorn Helgaas if (cmd & PCI_COMMAND_MEMORY) 7401d38fe6eSBjorn Helgaas vgadev->owns |= VGA_RSRC_LEGACY_MEM; 7411d38fe6eSBjorn Helgaas 7421d38fe6eSBjorn Helgaas /* Check if VGA cycles can get down to us */ 7431d38fe6eSBjorn Helgaas bus = pdev->bus; 7441d38fe6eSBjorn Helgaas while (bus) { 7451d38fe6eSBjorn Helgaas bridge = bus->self; 7461d38fe6eSBjorn Helgaas if (bridge) { 7471d38fe6eSBjorn Helgaas u16 l; 7481d38fe6eSBjorn Helgaas 7491d38fe6eSBjorn Helgaas pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l); 7501d38fe6eSBjorn Helgaas if (!(l & PCI_BRIDGE_CTL_VGA)) { 7511d38fe6eSBjorn Helgaas vgadev->owns = 0; 7521d38fe6eSBjorn Helgaas break; 7531d38fe6eSBjorn Helgaas } 7541d38fe6eSBjorn Helgaas } 7551d38fe6eSBjorn Helgaas bus = bus->parent; 7561d38fe6eSBjorn Helgaas } 7571d38fe6eSBjorn Helgaas 7581d38fe6eSBjorn Helgaas /* Deal with VGA default device. Use first enabled one 7591d38fe6eSBjorn Helgaas * by default if arch doesn't have it's own hook 7601d38fe6eSBjorn Helgaas */ 7611d38fe6eSBjorn Helgaas if (vga_default == NULL && 7621d38fe6eSBjorn Helgaas ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) { 7631d38fe6eSBjorn Helgaas vgaarb_info(&pdev->dev, "setting as boot VGA device\n"); 7641d38fe6eSBjorn Helgaas vga_set_default_device(pdev); 7651d38fe6eSBjorn Helgaas } 7661d38fe6eSBjorn Helgaas 7671d38fe6eSBjorn Helgaas vga_arbiter_check_bridge_sharing(vgadev); 7681d38fe6eSBjorn Helgaas 7691d38fe6eSBjorn Helgaas /* Add to the list */ 7701d38fe6eSBjorn Helgaas list_add_tail(&vgadev->list, &vga_list); 7711d38fe6eSBjorn Helgaas vga_count++; 7721d38fe6eSBjorn Helgaas vgaarb_info(&pdev->dev, "VGA device added: decodes=%s,owns=%s,locks=%s\n", 7731d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->decodes), 7741d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->owns), 7751d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->locks)); 7761d38fe6eSBjorn Helgaas 7771d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 7781d38fe6eSBjorn Helgaas return true; 7791d38fe6eSBjorn Helgaas fail: 7801d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 7811d38fe6eSBjorn Helgaas kfree(vgadev); 7821d38fe6eSBjorn Helgaas return false; 7831d38fe6eSBjorn Helgaas } 7841d38fe6eSBjorn Helgaas 7851d38fe6eSBjorn Helgaas static bool vga_arbiter_del_pci_device(struct pci_dev *pdev) 7861d38fe6eSBjorn Helgaas { 7871d38fe6eSBjorn Helgaas struct vga_device *vgadev; 7881d38fe6eSBjorn Helgaas unsigned long flags; 7891d38fe6eSBjorn Helgaas bool ret = true; 7901d38fe6eSBjorn Helgaas 7911d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 7921d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 7931d38fe6eSBjorn Helgaas if (vgadev == NULL) { 7941d38fe6eSBjorn Helgaas ret = false; 7951d38fe6eSBjorn Helgaas goto bail; 7961d38fe6eSBjorn Helgaas } 7971d38fe6eSBjorn Helgaas 7981d38fe6eSBjorn Helgaas if (vga_default == pdev) 7991d38fe6eSBjorn Helgaas vga_set_default_device(NULL); 8001d38fe6eSBjorn Helgaas 8011d38fe6eSBjorn Helgaas if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM)) 8021d38fe6eSBjorn Helgaas vga_decode_count--; 8031d38fe6eSBjorn Helgaas 8041d38fe6eSBjorn Helgaas /* Remove entry from list */ 8051d38fe6eSBjorn Helgaas list_del(&vgadev->list); 8061d38fe6eSBjorn Helgaas vga_count--; 8071d38fe6eSBjorn Helgaas /* Notify userland driver that the device is gone so it discards 8081d38fe6eSBjorn Helgaas * it's copies of the pci_dev pointer 8091d38fe6eSBjorn Helgaas */ 8101d38fe6eSBjorn Helgaas vga_arb_device_card_gone(pdev); 8111d38fe6eSBjorn Helgaas 8121d38fe6eSBjorn Helgaas /* Wake up all possible waiters */ 8131d38fe6eSBjorn Helgaas wake_up_all(&vga_wait_queue); 8141d38fe6eSBjorn Helgaas bail: 8151d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 8161d38fe6eSBjorn Helgaas kfree(vgadev); 8171d38fe6eSBjorn Helgaas return ret; 8181d38fe6eSBjorn Helgaas } 8191d38fe6eSBjorn Helgaas 8201d38fe6eSBjorn Helgaas /* this is called with the lock */ 8211d38fe6eSBjorn Helgaas static inline void vga_update_device_decodes(struct vga_device *vgadev, 8221d38fe6eSBjorn Helgaas int new_decodes) 8231d38fe6eSBjorn Helgaas { 8241d38fe6eSBjorn Helgaas struct device *dev = &vgadev->pdev->dev; 8251d38fe6eSBjorn Helgaas int old_decodes, decodes_removed, decodes_unlocked; 8261d38fe6eSBjorn Helgaas 8271d38fe6eSBjorn Helgaas old_decodes = vgadev->decodes; 8281d38fe6eSBjorn Helgaas decodes_removed = ~new_decodes & old_decodes; 8291d38fe6eSBjorn Helgaas decodes_unlocked = vgadev->locks & decodes_removed; 8301d38fe6eSBjorn Helgaas vgadev->decodes = new_decodes; 8311d38fe6eSBjorn Helgaas 8321d38fe6eSBjorn Helgaas vgaarb_info(dev, "changed VGA decodes: olddecodes=%s,decodes=%s:owns=%s\n", 8331d38fe6eSBjorn Helgaas vga_iostate_to_str(old_decodes), 8341d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->decodes), 8351d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->owns)); 8361d38fe6eSBjorn Helgaas 8371d38fe6eSBjorn Helgaas /* if we removed locked decodes, lock count goes to zero, and release */ 8381d38fe6eSBjorn Helgaas if (decodes_unlocked) { 8391d38fe6eSBjorn Helgaas if (decodes_unlocked & VGA_RSRC_LEGACY_IO) 8401d38fe6eSBjorn Helgaas vgadev->io_lock_cnt = 0; 8411d38fe6eSBjorn Helgaas if (decodes_unlocked & VGA_RSRC_LEGACY_MEM) 8421d38fe6eSBjorn Helgaas vgadev->mem_lock_cnt = 0; 8431d38fe6eSBjorn Helgaas __vga_put(vgadev, decodes_unlocked); 8441d38fe6eSBjorn Helgaas } 8451d38fe6eSBjorn Helgaas 8461d38fe6eSBjorn Helgaas /* change decodes counter */ 8471d38fe6eSBjorn Helgaas if (old_decodes & VGA_RSRC_LEGACY_MASK && 8481d38fe6eSBjorn Helgaas !(new_decodes & VGA_RSRC_LEGACY_MASK)) 8491d38fe6eSBjorn Helgaas vga_decode_count--; 8501d38fe6eSBjorn Helgaas if (!(old_decodes & VGA_RSRC_LEGACY_MASK) && 8511d38fe6eSBjorn Helgaas new_decodes & VGA_RSRC_LEGACY_MASK) 8521d38fe6eSBjorn Helgaas vga_decode_count++; 8531d38fe6eSBjorn Helgaas vgaarb_dbg(dev, "decoding count now is: %d\n", vga_decode_count); 8541d38fe6eSBjorn Helgaas } 8551d38fe6eSBjorn Helgaas 8561d38fe6eSBjorn Helgaas static void __vga_set_legacy_decoding(struct pci_dev *pdev, 8571d38fe6eSBjorn Helgaas unsigned int decodes, 8581d38fe6eSBjorn Helgaas bool userspace) 8591d38fe6eSBjorn Helgaas { 8601d38fe6eSBjorn Helgaas struct vga_device *vgadev; 8611d38fe6eSBjorn Helgaas unsigned long flags; 8621d38fe6eSBjorn Helgaas 8631d38fe6eSBjorn Helgaas decodes &= VGA_RSRC_LEGACY_MASK; 8641d38fe6eSBjorn Helgaas 8651d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 8661d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 8671d38fe6eSBjorn Helgaas if (vgadev == NULL) 8681d38fe6eSBjorn Helgaas goto bail; 8691d38fe6eSBjorn Helgaas 8701d38fe6eSBjorn Helgaas /* don't let userspace futz with kernel driver decodes */ 8711d38fe6eSBjorn Helgaas if (userspace && vgadev->set_decode) 8721d38fe6eSBjorn Helgaas goto bail; 8731d38fe6eSBjorn Helgaas 8741d38fe6eSBjorn Helgaas /* update the device decodes + counter */ 8751d38fe6eSBjorn Helgaas vga_update_device_decodes(vgadev, decodes); 8761d38fe6eSBjorn Helgaas 8771d38fe6eSBjorn Helgaas /* XXX if somebody is going from "doesn't decode" to "decodes" state 8781d38fe6eSBjorn Helgaas * here, additional care must be taken as we may have pending owner 8791d38fe6eSBjorn Helgaas * ship of non-legacy region ... 8801d38fe6eSBjorn Helgaas */ 8811d38fe6eSBjorn Helgaas bail: 8821d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 8831d38fe6eSBjorn Helgaas } 8841d38fe6eSBjorn Helgaas 8851d38fe6eSBjorn Helgaas /** 8861d38fe6eSBjorn Helgaas * vga_set_legacy_decoding 8871d38fe6eSBjorn Helgaas * @pdev: pci device of the VGA card 8881d38fe6eSBjorn Helgaas * @decodes: bit mask of what legacy regions the card decodes 8891d38fe6eSBjorn Helgaas * 8901d38fe6eSBjorn Helgaas * Indicates to the arbiter if the card decodes legacy VGA IOs, legacy VGA 8911d38fe6eSBjorn Helgaas * Memory, both, or none. All cards default to both, the card driver (fbdev for 8921d38fe6eSBjorn Helgaas * example) should tell the arbiter if it has disabled legacy decoding, so the 8931d38fe6eSBjorn Helgaas * card can be left out of the arbitration process (and can be safe to take 8941d38fe6eSBjorn Helgaas * interrupts at any time. 8951d38fe6eSBjorn Helgaas */ 8961d38fe6eSBjorn Helgaas void vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes) 8971d38fe6eSBjorn Helgaas { 8981d38fe6eSBjorn Helgaas __vga_set_legacy_decoding(pdev, decodes, false); 8991d38fe6eSBjorn Helgaas } 9001d38fe6eSBjorn Helgaas EXPORT_SYMBOL(vga_set_legacy_decoding); 9011d38fe6eSBjorn Helgaas 9021d38fe6eSBjorn Helgaas /** 9031d38fe6eSBjorn Helgaas * vga_client_register - register or unregister a VGA arbitration client 9041d38fe6eSBjorn Helgaas * @pdev: pci device of the VGA client 9051d38fe6eSBjorn Helgaas * @set_decode: vga decode change callback 9061d38fe6eSBjorn Helgaas * 9071d38fe6eSBjorn Helgaas * Clients have two callback mechanisms they can use. 9081d38fe6eSBjorn Helgaas * 9091d38fe6eSBjorn Helgaas * @set_decode callback: If a client can disable its GPU VGA resource, it 9101d38fe6eSBjorn Helgaas * will get a callback from this to set the encode/decode state. 9111d38fe6eSBjorn Helgaas * 9121d38fe6eSBjorn Helgaas * Rationale: we cannot disable VGA decode resources unconditionally some single 9131d38fe6eSBjorn Helgaas * GPU laptops seem to require ACPI or BIOS access to the VGA registers to 9141d38fe6eSBjorn Helgaas * control things like backlights etc. Hopefully newer multi-GPU laptops do 9151d38fe6eSBjorn Helgaas * something saner, and desktops won't have any special ACPI for this. The 9161d38fe6eSBjorn Helgaas * driver will get a callback when VGA arbitration is first used by userspace 9171d38fe6eSBjorn Helgaas * since some older X servers have issues. 9181d38fe6eSBjorn Helgaas * 9191d38fe6eSBjorn Helgaas * This function does not check whether a client for @pdev has been registered 9201d38fe6eSBjorn Helgaas * already. 9211d38fe6eSBjorn Helgaas * 9221d38fe6eSBjorn Helgaas * To unregister just call vga_client_unregister(). 9231d38fe6eSBjorn Helgaas * 9241d38fe6eSBjorn Helgaas * Returns: 0 on success, -1 on failure 9251d38fe6eSBjorn Helgaas */ 9261d38fe6eSBjorn Helgaas int vga_client_register(struct pci_dev *pdev, 9271d38fe6eSBjorn Helgaas unsigned int (*set_decode)(struct pci_dev *pdev, bool decode)) 9281d38fe6eSBjorn Helgaas { 9291d38fe6eSBjorn Helgaas int ret = -ENODEV; 9301d38fe6eSBjorn Helgaas struct vga_device *vgadev; 9311d38fe6eSBjorn Helgaas unsigned long flags; 9321d38fe6eSBjorn Helgaas 9331d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 9341d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 9351d38fe6eSBjorn Helgaas if (!vgadev) 9361d38fe6eSBjorn Helgaas goto bail; 9371d38fe6eSBjorn Helgaas 9381d38fe6eSBjorn Helgaas vgadev->set_decode = set_decode; 9391d38fe6eSBjorn Helgaas ret = 0; 9401d38fe6eSBjorn Helgaas 9411d38fe6eSBjorn Helgaas bail: 9421d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 9431d38fe6eSBjorn Helgaas return ret; 9441d38fe6eSBjorn Helgaas 9451d38fe6eSBjorn Helgaas } 9461d38fe6eSBjorn Helgaas EXPORT_SYMBOL(vga_client_register); 9471d38fe6eSBjorn Helgaas 9481d38fe6eSBjorn Helgaas /* 9491d38fe6eSBjorn Helgaas * Char driver implementation 9501d38fe6eSBjorn Helgaas * 9511d38fe6eSBjorn Helgaas * Semantics is: 9521d38fe6eSBjorn Helgaas * 9531d38fe6eSBjorn Helgaas * open : open user instance of the arbitrer. by default, it's 9541d38fe6eSBjorn Helgaas * attached to the default VGA device of the system. 9551d38fe6eSBjorn Helgaas * 9561d38fe6eSBjorn Helgaas * close : close user instance, release locks 9571d38fe6eSBjorn Helgaas * 9581d38fe6eSBjorn Helgaas * read : return a string indicating the status of the target. 9591d38fe6eSBjorn Helgaas * an IO state string is of the form {io,mem,io+mem,none}, 9601d38fe6eSBjorn Helgaas * mc and ic are respectively mem and io lock counts (for 9611d38fe6eSBjorn Helgaas * debugging/diagnostic only). "decodes" indicate what the 9621d38fe6eSBjorn Helgaas * card currently decodes, "owns" indicates what is currently 9631d38fe6eSBjorn Helgaas * enabled on it, and "locks" indicates what is locked by this 9641d38fe6eSBjorn Helgaas * card. If the card is unplugged, we get "invalid" then for 9651d38fe6eSBjorn Helgaas * card_ID and an -ENODEV error is returned for any command 9661d38fe6eSBjorn Helgaas * until a new card is targeted 9671d38fe6eSBjorn Helgaas * 9681d38fe6eSBjorn Helgaas * "<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)" 9691d38fe6eSBjorn Helgaas * 9701d38fe6eSBjorn Helgaas * write : write a command to the arbiter. List of commands is: 9711d38fe6eSBjorn Helgaas * 9721d38fe6eSBjorn Helgaas * target <card_ID> : switch target to card <card_ID> (see below) 9731d38fe6eSBjorn Helgaas * lock <io_state> : acquires locks on target ("none" is invalid io_state) 9741d38fe6eSBjorn Helgaas * trylock <io_state> : non-blocking acquire locks on target 9751d38fe6eSBjorn Helgaas * unlock <io_state> : release locks on target 9761d38fe6eSBjorn Helgaas * unlock all : release all locks on target held by this user 9771d38fe6eSBjorn Helgaas * decodes <io_state> : set the legacy decoding attributes for the card 9781d38fe6eSBjorn Helgaas * 9791d38fe6eSBjorn Helgaas * poll : event if something change on any card (not just the target) 9801d38fe6eSBjorn Helgaas * 9811d38fe6eSBjorn Helgaas * card_ID is of the form "PCI:domain:bus:dev.fn". It can be set to "default" 9821d38fe6eSBjorn Helgaas * to go back to the system default card (TODO: not implemented yet). 9831d38fe6eSBjorn Helgaas * Currently, only PCI is supported as a prefix, but the userland API may 9841d38fe6eSBjorn Helgaas * support other bus types in the future, even if the current kernel 9851d38fe6eSBjorn Helgaas * implementation doesn't. 9861d38fe6eSBjorn Helgaas * 9871d38fe6eSBjorn Helgaas * Note about locks: 9881d38fe6eSBjorn Helgaas * 9891d38fe6eSBjorn Helgaas * The driver keeps track of which user has what locks on which card. It 9901d38fe6eSBjorn Helgaas * supports stacking, like the kernel one. This complexifies the implementation 9911d38fe6eSBjorn Helgaas * a bit, but makes the arbiter more tolerant to userspace problems and able 9921d38fe6eSBjorn Helgaas * to properly cleanup in all cases when a process dies. 9931d38fe6eSBjorn Helgaas * Currently, a max of 16 cards simultaneously can have locks issued from 9941d38fe6eSBjorn Helgaas * userspace for a given user (file descriptor instance) of the arbiter. 9951d38fe6eSBjorn Helgaas * 9961d38fe6eSBjorn Helgaas * If the device is hot-unplugged, there is a hook inside the module to notify 9971d38fe6eSBjorn Helgaas * they being added/removed in the system and automatically added/removed in 9981d38fe6eSBjorn Helgaas * the arbiter. 9991d38fe6eSBjorn Helgaas */ 10001d38fe6eSBjorn Helgaas 10011d38fe6eSBjorn Helgaas #define MAX_USER_CARDS CONFIG_VGA_ARB_MAX_GPUS 10021d38fe6eSBjorn Helgaas #define PCI_INVALID_CARD ((struct pci_dev *)-1UL) 10031d38fe6eSBjorn Helgaas 10041d38fe6eSBjorn Helgaas /* 10051d38fe6eSBjorn Helgaas * Each user has an array of these, tracking which cards have locks 10061d38fe6eSBjorn Helgaas */ 10071d38fe6eSBjorn Helgaas struct vga_arb_user_card { 10081d38fe6eSBjorn Helgaas struct pci_dev *pdev; 10091d38fe6eSBjorn Helgaas unsigned int mem_cnt; 10101d38fe6eSBjorn Helgaas unsigned int io_cnt; 10111d38fe6eSBjorn Helgaas }; 10121d38fe6eSBjorn Helgaas 10131d38fe6eSBjorn Helgaas struct vga_arb_private { 10141d38fe6eSBjorn Helgaas struct list_head list; 10151d38fe6eSBjorn Helgaas struct pci_dev *target; 10161d38fe6eSBjorn Helgaas struct vga_arb_user_card cards[MAX_USER_CARDS]; 10171d38fe6eSBjorn Helgaas spinlock_t lock; 10181d38fe6eSBjorn Helgaas }; 10191d38fe6eSBjorn Helgaas 10201d38fe6eSBjorn Helgaas static LIST_HEAD(vga_user_list); 10211d38fe6eSBjorn Helgaas static DEFINE_SPINLOCK(vga_user_lock); 10221d38fe6eSBjorn Helgaas 10231d38fe6eSBjorn Helgaas 10241d38fe6eSBjorn Helgaas /* 10251d38fe6eSBjorn Helgaas * This function gets a string in the format: "PCI:domain:bus:dev.fn" and 10261d38fe6eSBjorn Helgaas * returns the respective values. If the string is not in this format, 10271d38fe6eSBjorn Helgaas * it returns 0. 10281d38fe6eSBjorn Helgaas */ 10291d38fe6eSBjorn Helgaas static int vga_pci_str_to_vars(char *buf, int count, unsigned int *domain, 10301d38fe6eSBjorn Helgaas unsigned int *bus, unsigned int *devfn) 10311d38fe6eSBjorn Helgaas { 10321d38fe6eSBjorn Helgaas int n; 10331d38fe6eSBjorn Helgaas unsigned int slot, func; 10341d38fe6eSBjorn Helgaas 10351d38fe6eSBjorn Helgaas 10361d38fe6eSBjorn Helgaas n = sscanf(buf, "PCI:%x:%x:%x.%x", domain, bus, &slot, &func); 10371d38fe6eSBjorn Helgaas if (n != 4) 10381d38fe6eSBjorn Helgaas return 0; 10391d38fe6eSBjorn Helgaas 10401d38fe6eSBjorn Helgaas *devfn = PCI_DEVFN(slot, func); 10411d38fe6eSBjorn Helgaas 10421d38fe6eSBjorn Helgaas return 1; 10431d38fe6eSBjorn Helgaas } 10441d38fe6eSBjorn Helgaas 10451d38fe6eSBjorn Helgaas static ssize_t vga_arb_read(struct file *file, char __user *buf, 10461d38fe6eSBjorn Helgaas size_t count, loff_t *ppos) 10471d38fe6eSBjorn Helgaas { 10481d38fe6eSBjorn Helgaas struct vga_arb_private *priv = file->private_data; 10491d38fe6eSBjorn Helgaas struct vga_device *vgadev; 10501d38fe6eSBjorn Helgaas struct pci_dev *pdev; 10511d38fe6eSBjorn Helgaas unsigned long flags; 10521d38fe6eSBjorn Helgaas size_t len; 10531d38fe6eSBjorn Helgaas int rc; 10541d38fe6eSBjorn Helgaas char *lbuf; 10551d38fe6eSBjorn Helgaas 10561d38fe6eSBjorn Helgaas lbuf = kmalloc(1024, GFP_KERNEL); 10571d38fe6eSBjorn Helgaas if (lbuf == NULL) 10581d38fe6eSBjorn Helgaas return -ENOMEM; 10591d38fe6eSBjorn Helgaas 10601d38fe6eSBjorn Helgaas /* Shields against vga_arb_device_card_gone (pci_dev going 10611d38fe6eSBjorn Helgaas * away), and allows access to vga list 10621d38fe6eSBjorn Helgaas */ 10631d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 10641d38fe6eSBjorn Helgaas 10651d38fe6eSBjorn Helgaas /* If we are targeting the default, use it */ 10661d38fe6eSBjorn Helgaas pdev = priv->target; 10671d38fe6eSBjorn Helgaas if (pdev == NULL || pdev == PCI_INVALID_CARD) { 10681d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 10691d38fe6eSBjorn Helgaas len = sprintf(lbuf, "invalid"); 10701d38fe6eSBjorn Helgaas goto done; 10711d38fe6eSBjorn Helgaas } 10721d38fe6eSBjorn Helgaas 10731d38fe6eSBjorn Helgaas /* Find card vgadev structure */ 10741d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 10751d38fe6eSBjorn Helgaas if (vgadev == NULL) { 10761d38fe6eSBjorn Helgaas /* Wow, it's not in the list, that shouldn't happen, 10771d38fe6eSBjorn Helgaas * let's fix us up and return invalid card 10781d38fe6eSBjorn Helgaas */ 10791d38fe6eSBjorn Helgaas if (pdev == priv->target) 10801d38fe6eSBjorn Helgaas vga_arb_device_card_gone(pdev); 10811d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 10821d38fe6eSBjorn Helgaas len = sprintf(lbuf, "invalid"); 10831d38fe6eSBjorn Helgaas goto done; 10841d38fe6eSBjorn Helgaas } 10851d38fe6eSBjorn Helgaas 10861d38fe6eSBjorn Helgaas /* Fill the buffer with infos */ 10871d38fe6eSBjorn Helgaas len = snprintf(lbuf, 1024, 10881d38fe6eSBjorn Helgaas "count:%d,PCI:%s,decodes=%s,owns=%s,locks=%s(%d:%d)\n", 10891d38fe6eSBjorn Helgaas vga_decode_count, pci_name(pdev), 10901d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->decodes), 10911d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->owns), 10921d38fe6eSBjorn Helgaas vga_iostate_to_str(vgadev->locks), 10931d38fe6eSBjorn Helgaas vgadev->io_lock_cnt, vgadev->mem_lock_cnt); 10941d38fe6eSBjorn Helgaas 10951d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 10961d38fe6eSBjorn Helgaas done: 10971d38fe6eSBjorn Helgaas 10981d38fe6eSBjorn Helgaas /* Copy that to user */ 10991d38fe6eSBjorn Helgaas if (len > count) 11001d38fe6eSBjorn Helgaas len = count; 11011d38fe6eSBjorn Helgaas rc = copy_to_user(buf, lbuf, len); 11021d38fe6eSBjorn Helgaas kfree(lbuf); 11031d38fe6eSBjorn Helgaas if (rc) 11041d38fe6eSBjorn Helgaas return -EFAULT; 11051d38fe6eSBjorn Helgaas return len; 11061d38fe6eSBjorn Helgaas } 11071d38fe6eSBjorn Helgaas 11081d38fe6eSBjorn Helgaas /* 11091d38fe6eSBjorn Helgaas * TODO: To avoid parsing inside kernel and to improve the speed we may 11101d38fe6eSBjorn Helgaas * consider use ioctl here 11111d38fe6eSBjorn Helgaas */ 11121d38fe6eSBjorn Helgaas static ssize_t vga_arb_write(struct file *file, const char __user *buf, 11131d38fe6eSBjorn Helgaas size_t count, loff_t *ppos) 11141d38fe6eSBjorn Helgaas { 11151d38fe6eSBjorn Helgaas struct vga_arb_private *priv = file->private_data; 11161d38fe6eSBjorn Helgaas struct vga_arb_user_card *uc = NULL; 11171d38fe6eSBjorn Helgaas struct pci_dev *pdev; 11181d38fe6eSBjorn Helgaas 11191d38fe6eSBjorn Helgaas unsigned int io_state; 11201d38fe6eSBjorn Helgaas 11211d38fe6eSBjorn Helgaas char kbuf[64], *curr_pos; 11221d38fe6eSBjorn Helgaas size_t remaining = count; 11231d38fe6eSBjorn Helgaas 11241d38fe6eSBjorn Helgaas int ret_val; 11251d38fe6eSBjorn Helgaas int i; 11261d38fe6eSBjorn Helgaas 11271d38fe6eSBjorn Helgaas if (count >= sizeof(kbuf)) 11281d38fe6eSBjorn Helgaas return -EINVAL; 11291d38fe6eSBjorn Helgaas if (copy_from_user(kbuf, buf, count)) 11301d38fe6eSBjorn Helgaas return -EFAULT; 11311d38fe6eSBjorn Helgaas curr_pos = kbuf; 11321d38fe6eSBjorn Helgaas kbuf[count] = '\0'; /* Just to make sure... */ 11331d38fe6eSBjorn Helgaas 11341d38fe6eSBjorn Helgaas if (strncmp(curr_pos, "lock ", 5) == 0) { 11351d38fe6eSBjorn Helgaas curr_pos += 5; 11361d38fe6eSBjorn Helgaas remaining -= 5; 11371d38fe6eSBjorn Helgaas 11381d38fe6eSBjorn Helgaas pr_debug("client 0x%p called 'lock'\n", priv); 11391d38fe6eSBjorn Helgaas 11401d38fe6eSBjorn Helgaas if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) { 11411d38fe6eSBjorn Helgaas ret_val = -EPROTO; 11421d38fe6eSBjorn Helgaas goto done; 11431d38fe6eSBjorn Helgaas } 11441d38fe6eSBjorn Helgaas if (io_state == VGA_RSRC_NONE) { 11451d38fe6eSBjorn Helgaas ret_val = -EPROTO; 11461d38fe6eSBjorn Helgaas goto done; 11471d38fe6eSBjorn Helgaas } 11481d38fe6eSBjorn Helgaas 11491d38fe6eSBjorn Helgaas pdev = priv->target; 11501d38fe6eSBjorn Helgaas if (priv->target == NULL) { 11511d38fe6eSBjorn Helgaas ret_val = -ENODEV; 11521d38fe6eSBjorn Helgaas goto done; 11531d38fe6eSBjorn Helgaas } 11541d38fe6eSBjorn Helgaas 11551d38fe6eSBjorn Helgaas vga_get_uninterruptible(pdev, io_state); 11561d38fe6eSBjorn Helgaas 11571d38fe6eSBjorn Helgaas /* Update the client's locks lists... */ 11581d38fe6eSBjorn Helgaas for (i = 0; i < MAX_USER_CARDS; i++) { 11591d38fe6eSBjorn Helgaas if (priv->cards[i].pdev == pdev) { 11601d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_IO) 11611d38fe6eSBjorn Helgaas priv->cards[i].io_cnt++; 11621d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_MEM) 11631d38fe6eSBjorn Helgaas priv->cards[i].mem_cnt++; 11641d38fe6eSBjorn Helgaas break; 11651d38fe6eSBjorn Helgaas } 11661d38fe6eSBjorn Helgaas } 11671d38fe6eSBjorn Helgaas 11681d38fe6eSBjorn Helgaas ret_val = count; 11691d38fe6eSBjorn Helgaas goto done; 11701d38fe6eSBjorn Helgaas } else if (strncmp(curr_pos, "unlock ", 7) == 0) { 11711d38fe6eSBjorn Helgaas curr_pos += 7; 11721d38fe6eSBjorn Helgaas remaining -= 7; 11731d38fe6eSBjorn Helgaas 11741d38fe6eSBjorn Helgaas pr_debug("client 0x%p called 'unlock'\n", priv); 11751d38fe6eSBjorn Helgaas 11761d38fe6eSBjorn Helgaas if (strncmp(curr_pos, "all", 3) == 0) 11771d38fe6eSBjorn Helgaas io_state = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; 11781d38fe6eSBjorn Helgaas else { 11791d38fe6eSBjorn Helgaas if (!vga_str_to_iostate 11801d38fe6eSBjorn Helgaas (curr_pos, remaining, &io_state)) { 11811d38fe6eSBjorn Helgaas ret_val = -EPROTO; 11821d38fe6eSBjorn Helgaas goto done; 11831d38fe6eSBjorn Helgaas } 11841d38fe6eSBjorn Helgaas /* TODO: Add this? 11851d38fe6eSBjorn Helgaas if (io_state == VGA_RSRC_NONE) { 11861d38fe6eSBjorn Helgaas ret_val = -EPROTO; 11871d38fe6eSBjorn Helgaas goto done; 11881d38fe6eSBjorn Helgaas } 11891d38fe6eSBjorn Helgaas */ 11901d38fe6eSBjorn Helgaas } 11911d38fe6eSBjorn Helgaas 11921d38fe6eSBjorn Helgaas pdev = priv->target; 11931d38fe6eSBjorn Helgaas if (priv->target == NULL) { 11941d38fe6eSBjorn Helgaas ret_val = -ENODEV; 11951d38fe6eSBjorn Helgaas goto done; 11961d38fe6eSBjorn Helgaas } 11971d38fe6eSBjorn Helgaas for (i = 0; i < MAX_USER_CARDS; i++) { 11981d38fe6eSBjorn Helgaas if (priv->cards[i].pdev == pdev) 11991d38fe6eSBjorn Helgaas uc = &priv->cards[i]; 12001d38fe6eSBjorn Helgaas } 12011d38fe6eSBjorn Helgaas 12021d38fe6eSBjorn Helgaas if (!uc) { 12031d38fe6eSBjorn Helgaas ret_val = -EINVAL; 12041d38fe6eSBjorn Helgaas goto done; 12051d38fe6eSBjorn Helgaas } 12061d38fe6eSBjorn Helgaas 12071d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0) { 12081d38fe6eSBjorn Helgaas ret_val = -EINVAL; 12091d38fe6eSBjorn Helgaas goto done; 12101d38fe6eSBjorn Helgaas } 12111d38fe6eSBjorn Helgaas 12121d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0) { 12131d38fe6eSBjorn Helgaas ret_val = -EINVAL; 12141d38fe6eSBjorn Helgaas goto done; 12151d38fe6eSBjorn Helgaas } 12161d38fe6eSBjorn Helgaas 12171d38fe6eSBjorn Helgaas vga_put(pdev, io_state); 12181d38fe6eSBjorn Helgaas 12191d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_IO) 12201d38fe6eSBjorn Helgaas uc->io_cnt--; 12211d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_MEM) 12221d38fe6eSBjorn Helgaas uc->mem_cnt--; 12231d38fe6eSBjorn Helgaas 12241d38fe6eSBjorn Helgaas ret_val = count; 12251d38fe6eSBjorn Helgaas goto done; 12261d38fe6eSBjorn Helgaas } else if (strncmp(curr_pos, "trylock ", 8) == 0) { 12271d38fe6eSBjorn Helgaas curr_pos += 8; 12281d38fe6eSBjorn Helgaas remaining -= 8; 12291d38fe6eSBjorn Helgaas 12301d38fe6eSBjorn Helgaas pr_debug("client 0x%p called 'trylock'\n", priv); 12311d38fe6eSBjorn Helgaas 12321d38fe6eSBjorn Helgaas if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) { 12331d38fe6eSBjorn Helgaas ret_val = -EPROTO; 12341d38fe6eSBjorn Helgaas goto done; 12351d38fe6eSBjorn Helgaas } 12361d38fe6eSBjorn Helgaas /* TODO: Add this? 12371d38fe6eSBjorn Helgaas if (io_state == VGA_RSRC_NONE) { 12381d38fe6eSBjorn Helgaas ret_val = -EPROTO; 12391d38fe6eSBjorn Helgaas goto done; 12401d38fe6eSBjorn Helgaas } 12411d38fe6eSBjorn Helgaas */ 12421d38fe6eSBjorn Helgaas 12431d38fe6eSBjorn Helgaas pdev = priv->target; 12441d38fe6eSBjorn Helgaas if (priv->target == NULL) { 12451d38fe6eSBjorn Helgaas ret_val = -ENODEV; 12461d38fe6eSBjorn Helgaas goto done; 12471d38fe6eSBjorn Helgaas } 12481d38fe6eSBjorn Helgaas 12491d38fe6eSBjorn Helgaas if (vga_tryget(pdev, io_state)) { 12501d38fe6eSBjorn Helgaas /* Update the client's locks lists... */ 12511d38fe6eSBjorn Helgaas for (i = 0; i < MAX_USER_CARDS; i++) { 12521d38fe6eSBjorn Helgaas if (priv->cards[i].pdev == pdev) { 12531d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_IO) 12541d38fe6eSBjorn Helgaas priv->cards[i].io_cnt++; 12551d38fe6eSBjorn Helgaas if (io_state & VGA_RSRC_LEGACY_MEM) 12561d38fe6eSBjorn Helgaas priv->cards[i].mem_cnt++; 12571d38fe6eSBjorn Helgaas break; 12581d38fe6eSBjorn Helgaas } 12591d38fe6eSBjorn Helgaas } 12601d38fe6eSBjorn Helgaas ret_val = count; 12611d38fe6eSBjorn Helgaas goto done; 12621d38fe6eSBjorn Helgaas } else { 12631d38fe6eSBjorn Helgaas ret_val = -EBUSY; 12641d38fe6eSBjorn Helgaas goto done; 12651d38fe6eSBjorn Helgaas } 12661d38fe6eSBjorn Helgaas 12671d38fe6eSBjorn Helgaas } else if (strncmp(curr_pos, "target ", 7) == 0) { 12681d38fe6eSBjorn Helgaas unsigned int domain, bus, devfn; 12691d38fe6eSBjorn Helgaas struct vga_device *vgadev; 12701d38fe6eSBjorn Helgaas 12711d38fe6eSBjorn Helgaas curr_pos += 7; 12721d38fe6eSBjorn Helgaas remaining -= 7; 12731d38fe6eSBjorn Helgaas pr_debug("client 0x%p called 'target'\n", priv); 12741d38fe6eSBjorn Helgaas /* if target is default */ 12751d38fe6eSBjorn Helgaas if (!strncmp(curr_pos, "default", 7)) 12761d38fe6eSBjorn Helgaas pdev = pci_dev_get(vga_default_device()); 12771d38fe6eSBjorn Helgaas else { 12781d38fe6eSBjorn Helgaas if (!vga_pci_str_to_vars(curr_pos, remaining, 12791d38fe6eSBjorn Helgaas &domain, &bus, &devfn)) { 12801d38fe6eSBjorn Helgaas ret_val = -EPROTO; 12811d38fe6eSBjorn Helgaas goto done; 12821d38fe6eSBjorn Helgaas } 12831d38fe6eSBjorn Helgaas pdev = pci_get_domain_bus_and_slot(domain, bus, devfn); 12841d38fe6eSBjorn Helgaas if (!pdev) { 12851d38fe6eSBjorn Helgaas pr_debug("invalid PCI address %04x:%02x:%02x.%x\n", 12861d38fe6eSBjorn Helgaas domain, bus, PCI_SLOT(devfn), 12871d38fe6eSBjorn Helgaas PCI_FUNC(devfn)); 12881d38fe6eSBjorn Helgaas ret_val = -ENODEV; 12891d38fe6eSBjorn Helgaas goto done; 12901d38fe6eSBjorn Helgaas } 12911d38fe6eSBjorn Helgaas 12921d38fe6eSBjorn Helgaas pr_debug("%s ==> %04x:%02x:%02x.%x pdev %p\n", curr_pos, 12931d38fe6eSBjorn Helgaas domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), 12941d38fe6eSBjorn Helgaas pdev); 12951d38fe6eSBjorn Helgaas } 12961d38fe6eSBjorn Helgaas 12971d38fe6eSBjorn Helgaas vgadev = vgadev_find(pdev); 12981d38fe6eSBjorn Helgaas pr_debug("vgadev %p\n", vgadev); 12991d38fe6eSBjorn Helgaas if (vgadev == NULL) { 13001d38fe6eSBjorn Helgaas if (pdev) { 13011d38fe6eSBjorn Helgaas vgaarb_dbg(&pdev->dev, "not a VGA device\n"); 13021d38fe6eSBjorn Helgaas pci_dev_put(pdev); 13031d38fe6eSBjorn Helgaas } 13041d38fe6eSBjorn Helgaas 13051d38fe6eSBjorn Helgaas ret_val = -ENODEV; 13061d38fe6eSBjorn Helgaas goto done; 13071d38fe6eSBjorn Helgaas } 13081d38fe6eSBjorn Helgaas 13091d38fe6eSBjorn Helgaas priv->target = pdev; 13101d38fe6eSBjorn Helgaas for (i = 0; i < MAX_USER_CARDS; i++) { 13111d38fe6eSBjorn Helgaas if (priv->cards[i].pdev == pdev) 13121d38fe6eSBjorn Helgaas break; 13131d38fe6eSBjorn Helgaas if (priv->cards[i].pdev == NULL) { 13141d38fe6eSBjorn Helgaas priv->cards[i].pdev = pdev; 13151d38fe6eSBjorn Helgaas priv->cards[i].io_cnt = 0; 13161d38fe6eSBjorn Helgaas priv->cards[i].mem_cnt = 0; 13171d38fe6eSBjorn Helgaas break; 13181d38fe6eSBjorn Helgaas } 13191d38fe6eSBjorn Helgaas } 13201d38fe6eSBjorn Helgaas if (i == MAX_USER_CARDS) { 13211d38fe6eSBjorn Helgaas vgaarb_dbg(&pdev->dev, "maximum user cards (%d) number reached, ignoring this one!\n", 13221d38fe6eSBjorn Helgaas MAX_USER_CARDS); 13231d38fe6eSBjorn Helgaas pci_dev_put(pdev); 13241d38fe6eSBjorn Helgaas /* XXX: which value to return? */ 13251d38fe6eSBjorn Helgaas ret_val = -ENOMEM; 13261d38fe6eSBjorn Helgaas goto done; 13271d38fe6eSBjorn Helgaas } 13281d38fe6eSBjorn Helgaas 13291d38fe6eSBjorn Helgaas ret_val = count; 13301d38fe6eSBjorn Helgaas pci_dev_put(pdev); 13311d38fe6eSBjorn Helgaas goto done; 13321d38fe6eSBjorn Helgaas 13331d38fe6eSBjorn Helgaas 13341d38fe6eSBjorn Helgaas } else if (strncmp(curr_pos, "decodes ", 8) == 0) { 13351d38fe6eSBjorn Helgaas curr_pos += 8; 13361d38fe6eSBjorn Helgaas remaining -= 8; 13371d38fe6eSBjorn Helgaas pr_debug("client 0x%p called 'decodes'\n", priv); 13381d38fe6eSBjorn Helgaas 13391d38fe6eSBjorn Helgaas if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) { 13401d38fe6eSBjorn Helgaas ret_val = -EPROTO; 13411d38fe6eSBjorn Helgaas goto done; 13421d38fe6eSBjorn Helgaas } 13431d38fe6eSBjorn Helgaas pdev = priv->target; 13441d38fe6eSBjorn Helgaas if (priv->target == NULL) { 13451d38fe6eSBjorn Helgaas ret_val = -ENODEV; 13461d38fe6eSBjorn Helgaas goto done; 13471d38fe6eSBjorn Helgaas } 13481d38fe6eSBjorn Helgaas 13491d38fe6eSBjorn Helgaas __vga_set_legacy_decoding(pdev, io_state, true); 13501d38fe6eSBjorn Helgaas ret_val = count; 13511d38fe6eSBjorn Helgaas goto done; 13521d38fe6eSBjorn Helgaas } 13531d38fe6eSBjorn Helgaas /* If we got here, the message written is not part of the protocol! */ 13541d38fe6eSBjorn Helgaas return -EPROTO; 13551d38fe6eSBjorn Helgaas 13561d38fe6eSBjorn Helgaas done: 13571d38fe6eSBjorn Helgaas return ret_val; 13581d38fe6eSBjorn Helgaas } 13591d38fe6eSBjorn Helgaas 13601d38fe6eSBjorn Helgaas static __poll_t vga_arb_fpoll(struct file *file, poll_table *wait) 13611d38fe6eSBjorn Helgaas { 13621d38fe6eSBjorn Helgaas pr_debug("%s\n", __func__); 13631d38fe6eSBjorn Helgaas 13641d38fe6eSBjorn Helgaas poll_wait(file, &vga_wait_queue, wait); 13651d38fe6eSBjorn Helgaas return EPOLLIN; 13661d38fe6eSBjorn Helgaas } 13671d38fe6eSBjorn Helgaas 13681d38fe6eSBjorn Helgaas static int vga_arb_open(struct inode *inode, struct file *file) 13691d38fe6eSBjorn Helgaas { 13701d38fe6eSBjorn Helgaas struct vga_arb_private *priv; 13711d38fe6eSBjorn Helgaas unsigned long flags; 13721d38fe6eSBjorn Helgaas 13731d38fe6eSBjorn Helgaas pr_debug("%s\n", __func__); 13741d38fe6eSBjorn Helgaas 13751d38fe6eSBjorn Helgaas priv = kzalloc(sizeof(*priv), GFP_KERNEL); 13761d38fe6eSBjorn Helgaas if (priv == NULL) 13771d38fe6eSBjorn Helgaas return -ENOMEM; 13781d38fe6eSBjorn Helgaas spin_lock_init(&priv->lock); 13791d38fe6eSBjorn Helgaas file->private_data = priv; 13801d38fe6eSBjorn Helgaas 13811d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_user_lock, flags); 13821d38fe6eSBjorn Helgaas list_add(&priv->list, &vga_user_list); 13831d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_user_lock, flags); 13841d38fe6eSBjorn Helgaas 13851d38fe6eSBjorn Helgaas /* Set the client' lists of locks */ 13861d38fe6eSBjorn Helgaas priv->target = vga_default_device(); /* Maybe this is still null! */ 13871d38fe6eSBjorn Helgaas priv->cards[0].pdev = priv->target; 13881d38fe6eSBjorn Helgaas priv->cards[0].io_cnt = 0; 13891d38fe6eSBjorn Helgaas priv->cards[0].mem_cnt = 0; 13901d38fe6eSBjorn Helgaas 13911d38fe6eSBjorn Helgaas 13921d38fe6eSBjorn Helgaas return 0; 13931d38fe6eSBjorn Helgaas } 13941d38fe6eSBjorn Helgaas 13951d38fe6eSBjorn Helgaas static int vga_arb_release(struct inode *inode, struct file *file) 13961d38fe6eSBjorn Helgaas { 13971d38fe6eSBjorn Helgaas struct vga_arb_private *priv = file->private_data; 13981d38fe6eSBjorn Helgaas struct vga_arb_user_card *uc; 13991d38fe6eSBjorn Helgaas unsigned long flags; 14001d38fe6eSBjorn Helgaas int i; 14011d38fe6eSBjorn Helgaas 14021d38fe6eSBjorn Helgaas pr_debug("%s\n", __func__); 14031d38fe6eSBjorn Helgaas 14041d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_user_lock, flags); 14051d38fe6eSBjorn Helgaas list_del(&priv->list); 14061d38fe6eSBjorn Helgaas for (i = 0; i < MAX_USER_CARDS; i++) { 14071d38fe6eSBjorn Helgaas uc = &priv->cards[i]; 14081d38fe6eSBjorn Helgaas if (uc->pdev == NULL) 14091d38fe6eSBjorn Helgaas continue; 14101d38fe6eSBjorn Helgaas vgaarb_dbg(&uc->pdev->dev, "uc->io_cnt == %d, uc->mem_cnt == %d\n", 14111d38fe6eSBjorn Helgaas uc->io_cnt, uc->mem_cnt); 14121d38fe6eSBjorn Helgaas while (uc->io_cnt--) 14131d38fe6eSBjorn Helgaas vga_put(uc->pdev, VGA_RSRC_LEGACY_IO); 14141d38fe6eSBjorn Helgaas while (uc->mem_cnt--) 14151d38fe6eSBjorn Helgaas vga_put(uc->pdev, VGA_RSRC_LEGACY_MEM); 14161d38fe6eSBjorn Helgaas } 14171d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_user_lock, flags); 14181d38fe6eSBjorn Helgaas 14191d38fe6eSBjorn Helgaas kfree(priv); 14201d38fe6eSBjorn Helgaas 14211d38fe6eSBjorn Helgaas return 0; 14221d38fe6eSBjorn Helgaas } 14231d38fe6eSBjorn Helgaas 14241d38fe6eSBjorn Helgaas static void vga_arb_device_card_gone(struct pci_dev *pdev) 14251d38fe6eSBjorn Helgaas { 14261d38fe6eSBjorn Helgaas } 14271d38fe6eSBjorn Helgaas 14281d38fe6eSBjorn Helgaas /* 14291d38fe6eSBjorn Helgaas * callback any registered clients to let them know we have a 14301d38fe6eSBjorn Helgaas * change in VGA cards 14311d38fe6eSBjorn Helgaas */ 14321d38fe6eSBjorn Helgaas static void vga_arbiter_notify_clients(void) 14331d38fe6eSBjorn Helgaas { 14341d38fe6eSBjorn Helgaas struct vga_device *vgadev; 14351d38fe6eSBjorn Helgaas unsigned long flags; 14361d38fe6eSBjorn Helgaas uint32_t new_decodes; 14371d38fe6eSBjorn Helgaas bool new_state; 14381d38fe6eSBjorn Helgaas 14391d38fe6eSBjorn Helgaas if (!vga_arbiter_used) 14401d38fe6eSBjorn Helgaas return; 14411d38fe6eSBjorn Helgaas 14421d38fe6eSBjorn Helgaas spin_lock_irqsave(&vga_lock, flags); 14431d38fe6eSBjorn Helgaas list_for_each_entry(vgadev, &vga_list, list) { 14441d38fe6eSBjorn Helgaas if (vga_count > 1) 14451d38fe6eSBjorn Helgaas new_state = false; 14461d38fe6eSBjorn Helgaas else 14471d38fe6eSBjorn Helgaas new_state = true; 14481d38fe6eSBjorn Helgaas if (vgadev->set_decode) { 14491d38fe6eSBjorn Helgaas new_decodes = vgadev->set_decode(vgadev->pdev, 14501d38fe6eSBjorn Helgaas new_state); 14511d38fe6eSBjorn Helgaas vga_update_device_decodes(vgadev, new_decodes); 14521d38fe6eSBjorn Helgaas } 14531d38fe6eSBjorn Helgaas } 14541d38fe6eSBjorn Helgaas spin_unlock_irqrestore(&vga_lock, flags); 14551d38fe6eSBjorn Helgaas } 14561d38fe6eSBjorn Helgaas 14571d38fe6eSBjorn Helgaas static int pci_notify(struct notifier_block *nb, unsigned long action, 14581d38fe6eSBjorn Helgaas void *data) 14591d38fe6eSBjorn Helgaas { 14601d38fe6eSBjorn Helgaas struct device *dev = data; 14611d38fe6eSBjorn Helgaas struct pci_dev *pdev = to_pci_dev(dev); 14621d38fe6eSBjorn Helgaas bool notify = false; 14631d38fe6eSBjorn Helgaas 14641d38fe6eSBjorn Helgaas vgaarb_dbg(dev, "%s\n", __func__); 14651d38fe6eSBjorn Helgaas 14661d38fe6eSBjorn Helgaas /* For now we're only intereted in devices added and removed. I didn't 14671d38fe6eSBjorn Helgaas * test this thing here, so someone needs to double check for the 14681d38fe6eSBjorn Helgaas * cases of hotplugable vga cards. */ 14691d38fe6eSBjorn Helgaas if (action == BUS_NOTIFY_ADD_DEVICE) 14701d38fe6eSBjorn Helgaas notify = vga_arbiter_add_pci_device(pdev); 14711d38fe6eSBjorn Helgaas else if (action == BUS_NOTIFY_DEL_DEVICE) 14721d38fe6eSBjorn Helgaas notify = vga_arbiter_del_pci_device(pdev); 14731d38fe6eSBjorn Helgaas 14741d38fe6eSBjorn Helgaas if (notify) 14751d38fe6eSBjorn Helgaas vga_arbiter_notify_clients(); 14761d38fe6eSBjorn Helgaas return 0; 14771d38fe6eSBjorn Helgaas } 14781d38fe6eSBjorn Helgaas 14791d38fe6eSBjorn Helgaas static struct notifier_block pci_notifier = { 14801d38fe6eSBjorn Helgaas .notifier_call = pci_notify, 14811d38fe6eSBjorn Helgaas }; 14821d38fe6eSBjorn Helgaas 14831d38fe6eSBjorn Helgaas static const struct file_operations vga_arb_device_fops = { 14841d38fe6eSBjorn Helgaas .read = vga_arb_read, 14851d38fe6eSBjorn Helgaas .write = vga_arb_write, 14861d38fe6eSBjorn Helgaas .poll = vga_arb_fpoll, 14871d38fe6eSBjorn Helgaas .open = vga_arb_open, 14881d38fe6eSBjorn Helgaas .release = vga_arb_release, 14891d38fe6eSBjorn Helgaas .llseek = noop_llseek, 14901d38fe6eSBjorn Helgaas }; 14911d38fe6eSBjorn Helgaas 14921d38fe6eSBjorn Helgaas static struct miscdevice vga_arb_device = { 14931d38fe6eSBjorn Helgaas MISC_DYNAMIC_MINOR, "vga_arbiter", &vga_arb_device_fops 14941d38fe6eSBjorn Helgaas }; 14951d38fe6eSBjorn Helgaas 14961d38fe6eSBjorn Helgaas static void __init vga_arb_select_default_device(void) 14971d38fe6eSBjorn Helgaas { 14981d38fe6eSBjorn Helgaas struct pci_dev *pdev, *found = NULL; 14991d38fe6eSBjorn Helgaas struct vga_device *vgadev; 15001d38fe6eSBjorn Helgaas 15011d38fe6eSBjorn Helgaas list_for_each_entry(vgadev, &vga_list, list) { 1502*60a9bac8SBjorn Helgaas vga_select_framebuffer_device(vgadev->pdev); 15031d38fe6eSBjorn Helgaas } 15041d38fe6eSBjorn Helgaas 15051d38fe6eSBjorn Helgaas if (!vga_default_device()) { 15061d38fe6eSBjorn Helgaas list_for_each_entry_reverse(vgadev, &vga_list, list) { 15071d38fe6eSBjorn Helgaas struct device *dev = &vgadev->pdev->dev; 15081d38fe6eSBjorn Helgaas u16 cmd; 15091d38fe6eSBjorn Helgaas 15101d38fe6eSBjorn Helgaas pdev = vgadev->pdev; 15111d38fe6eSBjorn Helgaas pci_read_config_word(pdev, PCI_COMMAND, &cmd); 15121d38fe6eSBjorn Helgaas if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { 15131d38fe6eSBjorn Helgaas found = pdev; 15141d38fe6eSBjorn Helgaas if (vga_arb_integrated_gpu(dev)) 15151d38fe6eSBjorn Helgaas break; 15161d38fe6eSBjorn Helgaas } 15171d38fe6eSBjorn Helgaas } 15181d38fe6eSBjorn Helgaas } 15191d38fe6eSBjorn Helgaas 15201d38fe6eSBjorn Helgaas if (found) { 15211d38fe6eSBjorn Helgaas vgaarb_info(&found->dev, "setting as boot device (VGA legacy resources not available)\n"); 15221d38fe6eSBjorn Helgaas vga_set_default_device(found); 15231d38fe6eSBjorn Helgaas return; 15241d38fe6eSBjorn Helgaas } 15251d38fe6eSBjorn Helgaas 15261d38fe6eSBjorn Helgaas if (!vga_default_device()) { 15271d38fe6eSBjorn Helgaas vgadev = list_first_entry_or_null(&vga_list, 15281d38fe6eSBjorn Helgaas struct vga_device, list); 15291d38fe6eSBjorn Helgaas if (vgadev) { 15301d38fe6eSBjorn Helgaas struct device *dev = &vgadev->pdev->dev; 15311d38fe6eSBjorn Helgaas vgaarb_info(dev, "setting as boot device (VGA legacy resources not available)\n"); 15321d38fe6eSBjorn Helgaas vga_set_default_device(vgadev->pdev); 15331d38fe6eSBjorn Helgaas } 15341d38fe6eSBjorn Helgaas } 15351d38fe6eSBjorn Helgaas } 15361d38fe6eSBjorn Helgaas 15371d38fe6eSBjorn Helgaas static int __init vga_arb_device_init(void) 15381d38fe6eSBjorn Helgaas { 15391d38fe6eSBjorn Helgaas int rc; 15401d38fe6eSBjorn Helgaas struct pci_dev *pdev; 15411d38fe6eSBjorn Helgaas struct vga_device *vgadev; 15421d38fe6eSBjorn Helgaas 15431d38fe6eSBjorn Helgaas rc = misc_register(&vga_arb_device); 15441d38fe6eSBjorn Helgaas if (rc < 0) 15451d38fe6eSBjorn Helgaas pr_err("error %d registering device\n", rc); 15461d38fe6eSBjorn Helgaas 15471d38fe6eSBjorn Helgaas bus_register_notifier(&pci_bus_type, &pci_notifier); 15481d38fe6eSBjorn Helgaas 15491d38fe6eSBjorn Helgaas /* We add all PCI devices satisfying VGA class in the arbiter by 15501d38fe6eSBjorn Helgaas * default */ 15511d38fe6eSBjorn Helgaas pdev = NULL; 15521d38fe6eSBjorn Helgaas while ((pdev = 15531d38fe6eSBjorn Helgaas pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 15541d38fe6eSBjorn Helgaas PCI_ANY_ID, pdev)) != NULL) 15551d38fe6eSBjorn Helgaas vga_arbiter_add_pci_device(pdev); 15561d38fe6eSBjorn Helgaas 15571d38fe6eSBjorn Helgaas list_for_each_entry(vgadev, &vga_list, list) { 15581d38fe6eSBjorn Helgaas struct device *dev = &vgadev->pdev->dev; 15591d38fe6eSBjorn Helgaas 15601d38fe6eSBjorn Helgaas if (vgadev->bridge_has_one_vga) 15611d38fe6eSBjorn Helgaas vgaarb_info(dev, "bridge control possible\n"); 15621d38fe6eSBjorn Helgaas else 15631d38fe6eSBjorn Helgaas vgaarb_info(dev, "no bridge control possible\n"); 15641d38fe6eSBjorn Helgaas } 15651d38fe6eSBjorn Helgaas 15661d38fe6eSBjorn Helgaas vga_arb_select_default_device(); 15671d38fe6eSBjorn Helgaas 15681d38fe6eSBjorn Helgaas pr_info("loaded\n"); 15691d38fe6eSBjorn Helgaas return rc; 15701d38fe6eSBjorn Helgaas } 15711d38fe6eSBjorn Helgaas subsys_initcall_sync(vga_arb_device_init); 1572