diff options
Diffstat (limited to 'drivers/gpu/vga')
-rw-r--r-- | drivers/gpu/vga/vga_switcheroo.c | 95 | ||||
-rw-r--r-- | drivers/gpu/vga/vgaarb.c | 142 |
2 files changed, 138 insertions, 99 deletions
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 37ac7b5db..21060668f 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -6,17 +6,19 @@ * Licensed under GPLv2 * * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs - - Switcher interface - methods require for ATPX and DCM - - switchto - this throws the output MUX switch - - discrete_set_power - sets the power state for the discrete card - - GPU driver interface - - set_gpu_state - this should do the equiv of s/r for the card - - this should *not* set the discrete power state - - switch_check - check if the device is in a position to switch now + * + * Switcher interface - methods require for ATPX and DCM + * - switchto - this throws the output MUX switch + * - discrete_set_power - sets the power state for the discrete card + * + * GPU driver interface + * - set_gpu_state - this should do the equiv of s/r for the card + * - this should *not* set the discrete power state + * - switch_check - check if the device is in a position to switch now */ +#define pr_fmt(fmt) "vga_switcheroo: " fmt + #include <linux/module.h> #include <linux/seq_file.h> #include <linux/uaccess.h> @@ -111,7 +113,7 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) vgasr_priv.handler = handler; if (vga_switcheroo_ready()) { - printk(KERN_INFO "vga_switcheroo: enabled\n"); + pr_info("enabled\n"); vga_switcheroo_enable(); } mutex_unlock(&vgasr_mutex); @@ -124,7 +126,7 @@ void vga_switcheroo_unregister_handler(void) mutex_lock(&vgasr_mutex); vgasr_priv.handler = NULL; if (vgasr_priv.active) { - pr_info("vga_switcheroo: disabled\n"); + pr_info("disabled\n"); vga_switcheroo_debugfs_fini(&vgasr_priv); vgasr_priv.active = false; } @@ -155,7 +157,7 @@ static int register_client(struct pci_dev *pdev, vgasr_priv.registered_clients++; if (vga_switcheroo_ready()) { - printk(KERN_INFO "vga_switcheroo: enabled\n"); + pr_info("enabled\n"); vga_switcheroo_enable(); } mutex_unlock(&vgasr_mutex); @@ -167,7 +169,8 @@ int vga_switcheroo_register_client(struct pci_dev *pdev, bool driver_power_control) { return register_client(pdev, ops, -1, - pdev == vga_default_device(), driver_power_control); + pdev == vga_default_device(), + driver_power_control); } EXPORT_SYMBOL(vga_switcheroo_register_client); @@ -183,6 +186,7 @@ static struct vga_switcheroo_client * find_client_from_pci(struct list_head *head, struct pci_dev *pdev) { struct vga_switcheroo_client *client; + list_for_each_entry(client, head, list) if (client->pdev == pdev) return client; @@ -193,6 +197,7 @@ static struct vga_switcheroo_client * find_client_from_id(struct list_head *head, int client_id) { struct vga_switcheroo_client *client; + list_for_each_entry(client, head, list) if (client->id == client_id) return client; @@ -203,6 +208,7 @@ static struct vga_switcheroo_client * find_active_client(struct list_head *head) { struct vga_switcheroo_client *client; + list_for_each_entry(client, head, list) if (client->active && client_is_vga(client)) return client; @@ -235,7 +241,7 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev) kfree(client); } if (vgasr_priv.active && vgasr_priv.registered_clients < 2) { - printk(KERN_INFO "vga_switcheroo: disabled\n"); + pr_info("disabled\n"); vga_switcheroo_debugfs_fini(&vgasr_priv); vgasr_priv.active = false; } @@ -260,10 +266,12 @@ static int vga_switcheroo_show(struct seq_file *m, void *v) { struct vga_switcheroo_client *client; int i = 0; + mutex_lock(&vgasr_mutex); list_for_each_entry(client, &vgasr_priv.clients, list) { seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i, - client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", + client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : + "IGD", client_is_vga(client) ? "" : "-Audio", client->active ? '+' : ' ', client->driver_power_control ? "Dyn" : "", @@ -347,6 +355,7 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) if (new_client->fb_info) { struct fb_event event; + console_lock(); event.info = new_client->fb_info; fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); @@ -375,7 +384,7 @@ static bool check_can_switch(void) list_for_each_entry(client, &vgasr_priv.clients, list) { if (!client->ops->can_switch(client->pdev)) { - printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id); + pr_err("client %x refused switch\n", client->id); return false; } } @@ -484,20 +493,20 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, if (can_switch) { ret = vga_switchto_stage1(client); if (ret) - printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret); + pr_err("switching failed stage 1 %d\n", ret); ret = vga_switchto_stage2(client); if (ret) - printk(KERN_ERR "vga_switcheroo: switching failed stage 2 %d\n", ret); + pr_err("switching failed stage 2 %d\n", ret); } else { - printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id); + pr_info("setting delayed switch to client %d\n", client->id); vgasr_priv.delayed_switch_active = true; vgasr_priv.delayed_client_id = client_id; ret = vga_switchto_stage1(client); if (ret) - printk(KERN_ERR "vga_switcheroo: delayed switching stage 1 failed %d\n", ret); + pr_err("delayed switching stage 1 failed %d\n", ret); } out: @@ -516,32 +525,32 @@ static const struct file_operations vga_switcheroo_debugfs_fops = { static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv) { - if (priv->switch_file) { - debugfs_remove(priv->switch_file); - priv->switch_file = NULL; - } - if (priv->debugfs_root) { - debugfs_remove(priv->debugfs_root); - priv->debugfs_root = NULL; - } + debugfs_remove(priv->switch_file); + priv->switch_file = NULL; + + debugfs_remove(priv->debugfs_root); + priv->debugfs_root = NULL; } static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv) { + static const char mp[] = "/sys/kernel/debug"; + /* already initialised */ if (priv->debugfs_root) return 0; priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL); if (!priv->debugfs_root) { - printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n"); + pr_err("Cannot create %s/vgaswitcheroo\n", mp); goto fail; } priv->switch_file = debugfs_create_file("switch", 0644, - priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops); + priv->debugfs_root, NULL, + &vga_switcheroo_debugfs_fops); if (!priv->switch_file) { - printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n"); + pr_err("cannot create %s/vgaswitcheroo/switch\n", mp); goto fail; } return 0; @@ -560,7 +569,8 @@ int vga_switcheroo_process_delayed_switch(void) if (!vgasr_priv.delayed_switch_active) goto err; - printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); + pr_info("processing delayed switch to %d\n", + vgasr_priv.delayed_client_id); client = find_client_from_id(&vgasr_priv.clients, vgasr_priv.delayed_client_id); @@ -569,7 +579,7 @@ int vga_switcheroo_process_delayed_switch(void) ret = vga_switchto_stage2(client); if (ret) - printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret); + pr_err("delayed switching failed stage 2 %d\n", ret); vgasr_priv.delayed_switch_active = false; err = 0; @@ -579,7 +589,8 @@ err: } EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); -static void vga_switcheroo_power_switch(struct pci_dev *pdev, enum vga_switcheroo_state state) +static void vga_switcheroo_power_switch(struct pci_dev *pdev, + enum vga_switcheroo_state state) { struct vga_switcheroo_client *client; @@ -598,7 +609,8 @@ static void vga_switcheroo_power_switch(struct pci_dev *pdev, enum vga_switchero /* force a PCI device to a certain state - mainly to turn off audio clients */ -void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) +void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, + enum vga_switcheroo_state dynamic) { struct vga_switcheroo_client *client; @@ -644,7 +656,8 @@ static int vga_switcheroo_runtime_resume(struct device *dev) /* this version is for the case where the power switch is separate to the device being powered down. */ -int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) +int vga_switcheroo_init_domain_pm_ops(struct device *dev, + struct dev_pm_domain *domain) { /* copy over all the bus versions */ if (dev->bus && dev->bus->pm) { @@ -675,7 +688,8 @@ static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) /* we need to check if we have to switch back on the video device so the audio device can come back */ list_for_each_entry(client, &vgasr_priv.clients, list) { - if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && client_is_vga(client)) { + if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && + client_is_vga(client)) { found = client; ret = pm_runtime_get_sync(&client->pdev->dev); if (ret) { @@ -695,12 +709,15 @@ static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) return ret; } -int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) +int +vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, + struct dev_pm_domain *domain) { /* copy over all the bus versions */ if (dev->bus && dev->bus->pm) { domain->ops = *dev->bus->pm; - domain->ops.runtime_resume = vga_switcheroo_runtime_resume_hdmi_audio; + domain->ops.runtime_resume = + vga_switcheroo_runtime_resume_hdmi_audio; dev->pm_domain = domain; return 0; diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index 7bcbf8636..a0b433456 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -29,6 +29,8 @@ * */ +#define pr_fmt(fmt) "vgaarb: " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/pci.h> @@ -134,7 +136,6 @@ struct pci_dev *vga_default_device(void) { return vga_default; } - EXPORT_SYMBOL_GPL(vga_default_device); void vga_set_default_device(struct pci_dev *pdev) @@ -298,9 +299,9 @@ enable_them: pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); - if (!vgadev->bridge_has_one_vga) { + if (!vgadev->bridge_has_one_vga) vga_irq_set_state(vgadev, true); - } + vgadev->owns |= wants; lock_them: vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK); @@ -452,15 +453,15 @@ bail: } EXPORT_SYMBOL(vga_put); -/* Rules for using a bridge to control a VGA descendant decoding: - if a bridge has only one VGA descendant then it can be used - to control the VGA routing for that device. - It should always use the bridge closest to the device to control it. - If a bridge has a direct VGA descendant, but also have a sub-bridge - VGA descendant then we cannot use that bridge to control the direct VGA descendant. - So for every device we register, we need to iterate all its parent bridges - so we can invalidate any devices using them properly. -*/ +/* + * Rules for using a bridge to control a VGA descendant decoding: if a bridge + * has only one VGA descendant then it can be used to control the VGA routing + * for that device. It should always use the bridge closest to the device to + * control it. If a bridge has a direct VGA descendant, but also have a sub- + * bridge VGA descendant then we cannot use that bridge to control the direct + * VGA descendant. So for every device we register, we need to iterate all + * its parent bridges so we can invalidate any devices using them properly. + */ static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev) { struct vga_device *same_bridge_vgadev; @@ -484,21 +485,26 @@ static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev) /* see if the share a bridge with this device */ if (new_bridge == bridge) { - /* if their direct parent bridge is the same - as any bridge of this device then it can't be used - for that device */ + /* + * If their direct parent bridge is the same + * as any bridge of this device then it can't + * be used for that device. + */ same_bridge_vgadev->bridge_has_one_vga = false; } - /* now iterate the previous devices bridge hierarchy */ - /* if the new devices parent bridge is in the other devices - hierarchy then we can't use it to control this device */ + /* + * Now iterate the previous devices bridge hierarchy. + * If the new devices parent bridge is in the other + * devices hierarchy then we can't use it to control + * this device + */ while (bus) { bridge = bus->self; - if (bridge) { - if (bridge == vgadev->pdev->bus->self) - vgadev->bridge_has_one_vga = false; - } + + if (bridge && bridge == vgadev->pdev->bus->self) + vgadev->bridge_has_one_vga = false; + bus = bus->parent; } } @@ -527,10 +533,10 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) /* Allocate structure */ vgadev = kmalloc(sizeof(struct vga_device), GFP_KERNEL); if (vgadev == NULL) { - pr_err("vgaarb: failed to allocate pci device\n"); - /* What to do on allocation failure ? For now, let's - * just do nothing, I'm not sure there is anything saner - * to be done + pr_err("failed to allocate pci device\n"); + /* + * What to do on allocation failure ? For now, let's just do + * nothing, I'm not sure there is anything saner to be done. */ return false; } @@ -566,8 +572,8 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) bridge = bus->self; if (bridge) { u16 l; - pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, - &l); + + pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &l); if (!(l & PCI_BRIDGE_CTL_VGA)) { vgadev->owns = 0; break; @@ -581,8 +587,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) */ if (vga_default == NULL && ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) { - pr_info("vgaarb: setting as boot device: PCI:%s\n", - pci_name(pdev)); + pr_info("setting as boot device: PCI:%s\n", pci_name(pdev)); vga_set_default_device(pdev); } @@ -591,7 +596,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) /* Add to the list */ list_add(&vgadev->list, &vga_list); vga_count++; - pr_info("vgaarb: device added: PCI:%s,decodes=%s,owns=%s,locks=%s\n", + pr_info("device added: PCI:%s,decodes=%s,owns=%s,locks=%s\n", pci_name(pdev), vga_iostate_to_str(vgadev->decodes), vga_iostate_to_str(vgadev->owns), @@ -651,7 +656,7 @@ static inline void vga_update_device_decodes(struct vga_device *vgadev, decodes_unlocked = vgadev->locks & decodes_removed; vgadev->decodes = new_decodes; - pr_info("vgaarb: device changed decodes: PCI:%s,olddecodes=%s,decodes=%s:owns=%s\n", + pr_info("device changed decodes: PCI:%s,olddecodes=%s,decodes=%s:owns=%s\n", pci_name(vgadev->pdev), vga_iostate_to_str(old_decodes), vga_iostate_to_str(vgadev->decodes), @@ -673,10 +678,12 @@ static inline void vga_update_device_decodes(struct vga_device *vgadev, if (!(old_decodes & VGA_RSRC_LEGACY_MASK) && new_decodes & VGA_RSRC_LEGACY_MASK) vga_decode_count++; - pr_debug("vgaarb: decoding count now is: %d\n", vga_decode_count); + pr_debug("decoding count now is: %d\n", vga_decode_count); } -static void __vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes, bool userspace) +static void __vga_set_legacy_decoding(struct pci_dev *pdev, + unsigned int decodes, + bool userspace) { struct vga_device *vgadev; unsigned long flags; @@ -712,7 +719,8 @@ EXPORT_SYMBOL(vga_set_legacy_decoding); /* call with NULL to unregister */ int vga_client_register(struct pci_dev *pdev, void *cookie, void (*irq_set_state)(void *cookie, bool state), - unsigned int (*set_vga_decode)(void *cookie, bool decode)) + unsigned int (*set_vga_decode)(void *cookie, + bool decode)) { int ret = -ENODEV; struct vga_device *vgadev; @@ -832,7 +840,7 @@ static int vga_pci_str_to_vars(char *buf, int count, unsigned int *domain, return 1; } -static ssize_t vga_arb_read(struct file *file, char __user * buf, +static ssize_t vga_arb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct vga_arb_private *priv = file->private_data; @@ -899,7 +907,7 @@ done: * TODO: To avoid parsing inside kernel and to improve the speed we may * consider use ioctl here */ -static ssize_t vga_arb_write(struct file *file, const char __user * buf, +static ssize_t vga_arb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct vga_arb_private *priv = file->private_data; @@ -1075,13 +1083,13 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf, ret_val = -EPROTO; goto done; } - pr_debug("vgaarb: %s ==> %x:%x:%x.%x\n", curr_pos, + pr_debug("%s ==> %x:%x:%x.%x\n", curr_pos, domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); pdev = pci_get_domain_bus_and_slot(domain, bus, devfn); - pr_debug("vgaarb: pdev %p\n", pdev); + pr_debug("pdev %p\n", pdev); if (!pdev) { - pr_err("vgaarb: invalid PCI address %x:%x:%x\n", + pr_err("invalid PCI address %x:%x:%x\n", domain, bus, devfn); ret_val = -ENODEV; goto done; @@ -1089,10 +1097,13 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf, } vgadev = vgadev_find(pdev); - pr_debug("vgaarb: vgadev %p\n", vgadev); + pr_debug("vgadev %p\n", vgadev); if (vgadev == NULL) { - pr_err("vgaarb: this pci device is not a vga device\n"); - pci_dev_put(pdev); + if (pdev) { + pr_err("this pci device is not a vga device\n"); + pci_dev_put(pdev); + } + ret_val = -ENODEV; goto done; } @@ -1109,7 +1120,7 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf, } } if (i == MAX_USER_CARDS) { - pr_err("vgaarb: maximum user cards (%d) number reached!\n", + pr_err("maximum user cards (%d) number reached!\n", MAX_USER_CARDS); pci_dev_put(pdev); /* XXX: which value to return? */ @@ -1125,7 +1136,7 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf, } else if (strncmp(curr_pos, "decodes ", 8) == 0) { curr_pos += 8; remaining -= 8; - pr_debug("vgaarb: client 0x%p called 'decodes'\n", priv); + pr_debug("client 0x%p called 'decodes'\n", priv); if (!vga_str_to_iostate(curr_pos, remaining, &io_state)) { ret_val = -EPROTO; @@ -1150,7 +1161,7 @@ done: return ret_val; } -static unsigned int vga_arb_fpoll(struct file *file, poll_table * wait) +static unsigned int vga_arb_fpoll(struct file *file, poll_table *wait) { struct vga_arb_private *priv = file->private_data; @@ -1246,7 +1257,8 @@ static void vga_arbiter_notify_clients(void) else new_state = true; if (vgadev->set_vga_decode) { - new_decodes = vgadev->set_vga_decode(vgadev->cookie, new_state); + new_decodes = vgadev->set_vga_decode(vgadev->cookie, + new_state); vga_update_device_decodes(vgadev, new_decodes); } } @@ -1300,7 +1312,7 @@ static int __init vga_arb_device_init(void) rc = misc_register(&vga_arb_device); if (rc < 0) - pr_err("vgaarb: error %d registering device\n", rc); + pr_err("error %d registering device\n", rc); bus_register_notifier(&pci_bus_type, &pci_notifier); @@ -1312,21 +1324,29 @@ static int __init vga_arb_device_init(void) PCI_ANY_ID, pdev)) != NULL) vga_arbiter_add_pci_device(pdev); - pr_info("vgaarb: loaded\n"); + pr_info("loaded\n"); list_for_each_entry(vgadev, &vga_list, list) { #if defined(CONFIG_X86) || defined(CONFIG_IA64) - /* Override I/O based detection done by vga_arbiter_add_pci_device() - * as it may take the wrong device (e.g. on Apple system under EFI). + /* + * Override vga_arbiter_add_pci_device()'s I/O based detection + * as it may take the wrong device (e.g. on Apple system under + * EFI). * - * Select the device owning the boot framebuffer if there is one. + * Select the device owning the boot framebuffer if there is + * one. */ - resource_size_t start, end; + resource_size_t start, end, limit; + unsigned long flags; int i; + limit = screen_info.lfb_base + screen_info.lfb_size; + /* Does firmware framebuffer belong to us? */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { - if (!(pci_resource_flags(vgadev->pdev, i) & IORESOURCE_MEM)) + flags = pci_resource_flags(vgadev->pdev, i); + + if ((flags & IORESOURCE_MEM) == 0) continue; start = pci_resource_start(vgadev->pdev, i); @@ -1335,22 +1355,24 @@ static int __init vga_arb_device_init(void) if (!start || !end) continue; - if (screen_info.lfb_base < start || - (screen_info.lfb_base + screen_info.lfb_size) >= end) + if (screen_info.lfb_base < start || limit >= end) continue; + if (!vga_default_device()) - pr_info("vgaarb: setting as boot device: PCI:%s\n", + pr_info("setting as boot device: PCI:%s\n", pci_name(vgadev->pdev)); else if (vgadev->pdev != vga_default_device()) - pr_info("vgaarb: overriding boot device: PCI:%s\n", + pr_info("overriding boot device: PCI:%s\n", pci_name(vgadev->pdev)); vga_set_default_device(vgadev->pdev); } #endif if (vgadev->bridge_has_one_vga) - pr_info("vgaarb: bridge control possible %s\n", pci_name(vgadev->pdev)); + pr_info("bridge control possible %s\n", + pci_name(vgadev->pdev)); else - pr_info("vgaarb: no bridge control possible %s\n", pci_name(vgadev->pdev)); + pr_info("no bridge control possible %s\n", + pci_name(vgadev->pdev)); } return rc; } |