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