16a9ee8afSDave Airlie /* 26a9ee8afSDave Airlie * Copyright (c) 2010 Red Hat Inc. 36a9ee8afSDave Airlie * Author : Dave Airlie <airlied@redhat.com> 46a9ee8afSDave Airlie * 56a9ee8afSDave Airlie * 66a9ee8afSDave Airlie * Licensed under GPLv2 76a9ee8afSDave Airlie * 86a9ee8afSDave Airlie * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs 96a9ee8afSDave Airlie 106a9ee8afSDave Airlie Switcher interface - methods require for ATPX and DCM 116a9ee8afSDave Airlie - switchto - this throws the output MUX switch 126a9ee8afSDave Airlie - discrete_set_power - sets the power state for the discrete card 136a9ee8afSDave Airlie 146a9ee8afSDave Airlie GPU driver interface 156a9ee8afSDave Airlie - set_gpu_state - this should do the equiv of s/r for the card 166a9ee8afSDave Airlie - this should *not* set the discrete power state 176a9ee8afSDave Airlie - switch_check - check if the device is in a position to switch now 186a9ee8afSDave Airlie */ 196a9ee8afSDave Airlie 206a9ee8afSDave Airlie #include <linux/module.h> 216a9ee8afSDave Airlie #include <linux/dmi.h> 226a9ee8afSDave Airlie #include <linux/seq_file.h> 236a9ee8afSDave Airlie #include <linux/uaccess.h> 246a9ee8afSDave Airlie #include <linux/fs.h> 256a9ee8afSDave Airlie #include <linux/debugfs.h> 266a9ee8afSDave Airlie #include <linux/fb.h> 276a9ee8afSDave Airlie 286a9ee8afSDave Airlie #include <linux/pci.h> 296a9ee8afSDave Airlie #include <linux/vga_switcheroo.h> 306a9ee8afSDave Airlie 312fbe8c7cSMatthew Garrett #include <linux/vgaarb.h> 322fbe8c7cSMatthew Garrett 336a9ee8afSDave Airlie struct vga_switcheroo_client { 346a9ee8afSDave Airlie struct pci_dev *pdev; 356a9ee8afSDave Airlie struct fb_info *fb_info; 366a9ee8afSDave Airlie int pwr_state; 3726ec685fSTakashi Iwai const struct vga_switcheroo_client_ops *ops; 386a9ee8afSDave Airlie int id; 396a9ee8afSDave Airlie bool active; 4079721e0aSTakashi Iwai struct list_head list; 416a9ee8afSDave Airlie }; 426a9ee8afSDave Airlie 436a9ee8afSDave Airlie static DEFINE_MUTEX(vgasr_mutex); 446a9ee8afSDave Airlie 456a9ee8afSDave Airlie struct vgasr_priv { 466a9ee8afSDave Airlie 476a9ee8afSDave Airlie bool active; 486a9ee8afSDave Airlie bool delayed_switch_active; 496a9ee8afSDave Airlie enum vga_switcheroo_client_id delayed_client_id; 506a9ee8afSDave Airlie 516a9ee8afSDave Airlie struct dentry *debugfs_root; 526a9ee8afSDave Airlie struct dentry *switch_file; 536a9ee8afSDave Airlie 546a9ee8afSDave Airlie int registered_clients; 5579721e0aSTakashi Iwai struct list_head clients; 566a9ee8afSDave Airlie 576a9ee8afSDave Airlie struct vga_switcheroo_handler *handler; 586a9ee8afSDave Airlie }; 596a9ee8afSDave Airlie 606a9ee8afSDave Airlie static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); 616a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); 626a9ee8afSDave Airlie 636a9ee8afSDave Airlie /* only one switcheroo per system */ 6479721e0aSTakashi Iwai static struct vgasr_priv vgasr_priv = { 6579721e0aSTakashi Iwai .clients = LIST_HEAD_INIT(vgasr_priv.clients), 6679721e0aSTakashi Iwai }; 676a9ee8afSDave Airlie 686a9ee8afSDave Airlie int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) 696a9ee8afSDave Airlie { 706a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 716a9ee8afSDave Airlie if (vgasr_priv.handler) { 726a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 736a9ee8afSDave Airlie return -EINVAL; 746a9ee8afSDave Airlie } 756a9ee8afSDave Airlie 766a9ee8afSDave Airlie vgasr_priv.handler = handler; 776a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 786a9ee8afSDave Airlie return 0; 796a9ee8afSDave Airlie } 806a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_register_handler); 816a9ee8afSDave Airlie 826a9ee8afSDave Airlie void vga_switcheroo_unregister_handler(void) 836a9ee8afSDave Airlie { 846a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 856a9ee8afSDave Airlie vgasr_priv.handler = NULL; 866a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 876a9ee8afSDave Airlie } 886a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_unregister_handler); 896a9ee8afSDave Airlie 906a9ee8afSDave Airlie static void vga_switcheroo_enable(void) 916a9ee8afSDave Airlie { 926a9ee8afSDave Airlie int ret; 9379721e0aSTakashi Iwai struct vga_switcheroo_client *client; 9479721e0aSTakashi Iwai 956a9ee8afSDave Airlie /* call the handler to init */ 966a9ee8afSDave Airlie vgasr_priv.handler->init(); 976a9ee8afSDave Airlie 9879721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 9979721e0aSTakashi Iwai ret = vgasr_priv.handler->get_client_id(client->pdev); 1006a9ee8afSDave Airlie if (ret < 0) 1016a9ee8afSDave Airlie return; 1026a9ee8afSDave Airlie 10379721e0aSTakashi Iwai client->id = ret; 1046a9ee8afSDave Airlie } 1056a9ee8afSDave Airlie vga_switcheroo_debugfs_init(&vgasr_priv); 1066a9ee8afSDave Airlie vgasr_priv.active = true; 1076a9ee8afSDave Airlie } 1086a9ee8afSDave Airlie 1096a9ee8afSDave Airlie int vga_switcheroo_register_client(struct pci_dev *pdev, 11026ec685fSTakashi Iwai const struct vga_switcheroo_client_ops *ops) 1116a9ee8afSDave Airlie { 11279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 11379721e0aSTakashi Iwai 11479721e0aSTakashi Iwai client = kzalloc(sizeof(*client), GFP_KERNEL); 11579721e0aSTakashi Iwai if (!client) 11679721e0aSTakashi Iwai return -ENOMEM; 11779721e0aSTakashi Iwai 11879721e0aSTakashi Iwai client->pwr_state = VGA_SWITCHEROO_ON; 11979721e0aSTakashi Iwai client->pdev = pdev; 12026ec685fSTakashi Iwai client->ops = ops; 12179721e0aSTakashi Iwai client->id = -1; 12279721e0aSTakashi Iwai if (pdev == vga_default_device()) 12379721e0aSTakashi Iwai client->active = true; 1246a9ee8afSDave Airlie 1256a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 12679721e0aSTakashi Iwai list_add_tail(&client->list, &vgasr_priv.clients); 12779721e0aSTakashi Iwai vgasr_priv.registered_clients++; 1286a9ee8afSDave Airlie 1296a9ee8afSDave Airlie /* if we get two clients + handler */ 13079721e0aSTakashi Iwai if (vgasr_priv.registered_clients == 2 && vgasr_priv.handler) { 1316a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: enabled\n"); 1326a9ee8afSDave Airlie vga_switcheroo_enable(); 1336a9ee8afSDave Airlie } 1346a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 1356a9ee8afSDave Airlie return 0; 1366a9ee8afSDave Airlie } 1376a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_register_client); 1386a9ee8afSDave Airlie 13979721e0aSTakashi Iwai static struct vga_switcheroo_client * 14079721e0aSTakashi Iwai find_client_from_pci(struct list_head *head, struct pci_dev *pdev) 14179721e0aSTakashi Iwai { 14279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 14379721e0aSTakashi Iwai list_for_each_entry(client, head, list) 14479721e0aSTakashi Iwai if (client->pdev == pdev) 14579721e0aSTakashi Iwai return client; 14679721e0aSTakashi Iwai return NULL; 14779721e0aSTakashi Iwai } 14879721e0aSTakashi Iwai 14979721e0aSTakashi Iwai static struct vga_switcheroo_client * 15079721e0aSTakashi Iwai find_client_from_id(struct list_head *head, int client_id) 15179721e0aSTakashi Iwai { 15279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 15379721e0aSTakashi Iwai list_for_each_entry(client, head, list) 15479721e0aSTakashi Iwai if (client->id == client_id) 15579721e0aSTakashi Iwai return client; 15679721e0aSTakashi Iwai return NULL; 15779721e0aSTakashi Iwai } 15879721e0aSTakashi Iwai 15979721e0aSTakashi Iwai static struct vga_switcheroo_client * 16079721e0aSTakashi Iwai find_active_client(struct list_head *head) 16179721e0aSTakashi Iwai { 16279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 16379721e0aSTakashi Iwai list_for_each_entry(client, head, list) 16479721e0aSTakashi Iwai if (client->active == true) 16579721e0aSTakashi Iwai return client; 16679721e0aSTakashi Iwai return NULL; 16779721e0aSTakashi Iwai } 16879721e0aSTakashi Iwai 1696a9ee8afSDave Airlie void vga_switcheroo_unregister_client(struct pci_dev *pdev) 1706a9ee8afSDave Airlie { 17179721e0aSTakashi Iwai struct vga_switcheroo_client *client; 1726a9ee8afSDave Airlie 1736a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 17479721e0aSTakashi Iwai client = find_client_from_pci(&vgasr_priv.clients, pdev); 17579721e0aSTakashi Iwai if (client) { 17679721e0aSTakashi Iwai list_del(&client->list); 17779721e0aSTakashi Iwai kfree(client); 17879721e0aSTakashi Iwai vgasr_priv.registered_clients--; 1796a9ee8afSDave Airlie } 1806a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: disabled\n"); 1816a9ee8afSDave Airlie vga_switcheroo_debugfs_fini(&vgasr_priv); 1826a9ee8afSDave Airlie vgasr_priv.active = false; 1836a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 1846a9ee8afSDave Airlie } 1856a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_unregister_client); 1866a9ee8afSDave Airlie 1876a9ee8afSDave Airlie void vga_switcheroo_client_fb_set(struct pci_dev *pdev, 1886a9ee8afSDave Airlie struct fb_info *info) 1896a9ee8afSDave Airlie { 19079721e0aSTakashi Iwai struct vga_switcheroo_client *client; 1916a9ee8afSDave Airlie 1926a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 19379721e0aSTakashi Iwai client = find_client_from_pci(&vgasr_priv.clients, pdev); 19479721e0aSTakashi Iwai if (client) 19579721e0aSTakashi Iwai client->fb_info = info; 1966a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 1976a9ee8afSDave Airlie } 1986a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_client_fb_set); 1996a9ee8afSDave Airlie 2006a9ee8afSDave Airlie static int vga_switcheroo_show(struct seq_file *m, void *v) 2016a9ee8afSDave Airlie { 20279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 20379721e0aSTakashi Iwai int i = 0; 2046a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 20579721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 2066c2df40eSDave Airlie seq_printf(m, "%d:%s:%c:%s:%s\n", i, 20779721e0aSTakashi Iwai client->id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", 20879721e0aSTakashi Iwai client->active ? '+' : ' ', 20979721e0aSTakashi Iwai client->pwr_state ? "Pwr" : "Off", 21079721e0aSTakashi Iwai pci_name(client->pdev)); 21179721e0aSTakashi Iwai i++; 2126a9ee8afSDave Airlie } 2136a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 2146a9ee8afSDave Airlie return 0; 2156a9ee8afSDave Airlie } 2166a9ee8afSDave Airlie 2176a9ee8afSDave Airlie static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file) 2186a9ee8afSDave Airlie { 2196a9ee8afSDave Airlie return single_open(file, vga_switcheroo_show, NULL); 2206a9ee8afSDave Airlie } 2216a9ee8afSDave Airlie 2226a9ee8afSDave Airlie static int vga_switchon(struct vga_switcheroo_client *client) 2236a9ee8afSDave Airlie { 2245cfb3c3aSDave Airlie if (vgasr_priv.handler->power_state) 2255cfb3c3aSDave Airlie vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); 2266a9ee8afSDave Airlie /* call the driver callback to turn on device */ 22726ec685fSTakashi Iwai client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); 2286a9ee8afSDave Airlie client->pwr_state = VGA_SWITCHEROO_ON; 2296a9ee8afSDave Airlie return 0; 2306a9ee8afSDave Airlie } 2316a9ee8afSDave Airlie 2326a9ee8afSDave Airlie static int vga_switchoff(struct vga_switcheroo_client *client) 2336a9ee8afSDave Airlie { 2346a9ee8afSDave Airlie /* call the driver callback to turn off device */ 23526ec685fSTakashi Iwai client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); 2365cfb3c3aSDave Airlie if (vgasr_priv.handler->power_state) 2376a9ee8afSDave Airlie vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); 2386a9ee8afSDave Airlie client->pwr_state = VGA_SWITCHEROO_OFF; 2396a9ee8afSDave Airlie return 0; 2406a9ee8afSDave Airlie } 2416a9ee8afSDave Airlie 24266b37c67SDave Airlie /* stage one happens before delay */ 24366b37c67SDave Airlie static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) 2446a9ee8afSDave Airlie { 24579721e0aSTakashi Iwai struct vga_switcheroo_client *active; 2466a9ee8afSDave Airlie 24779721e0aSTakashi Iwai active = find_active_client(&vgasr_priv.clients); 2486a9ee8afSDave Airlie if (!active) 2496a9ee8afSDave Airlie return 0; 2506a9ee8afSDave Airlie 2516a9ee8afSDave Airlie if (new_client->pwr_state == VGA_SWITCHEROO_OFF) 2526a9ee8afSDave Airlie vga_switchon(new_client); 2536a9ee8afSDave Airlie 2542fbe8c7cSMatthew Garrett vga_set_default_device(new_client->pdev); 2552fbe8c7cSMatthew Garrett 25666b37c67SDave Airlie return 0; 25766b37c67SDave Airlie } 25866b37c67SDave Airlie 25966b37c67SDave Airlie /* post delay */ 26066b37c67SDave Airlie static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) 26166b37c67SDave Airlie { 26266b37c67SDave Airlie int ret; 26379721e0aSTakashi Iwai struct vga_switcheroo_client *active; 26466b37c67SDave Airlie 26579721e0aSTakashi Iwai active = find_active_client(&vgasr_priv.clients); 26666b37c67SDave Airlie if (!active) 26766b37c67SDave Airlie return 0; 26866b37c67SDave Airlie 26966b37c67SDave Airlie active->active = false; 2706a9ee8afSDave Airlie 2716a9ee8afSDave Airlie if (new_client->fb_info) { 2726a9ee8afSDave Airlie struct fb_event event; 2736a9ee8afSDave Airlie event.info = new_client->fb_info; 2746a9ee8afSDave Airlie fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); 2756a9ee8afSDave Airlie } 2766a9ee8afSDave Airlie 2776a9ee8afSDave Airlie ret = vgasr_priv.handler->switchto(new_client->id); 2786a9ee8afSDave Airlie if (ret) 2796a9ee8afSDave Airlie return ret; 2806a9ee8afSDave Airlie 28126ec685fSTakashi Iwai if (new_client->ops->reprobe) 28226ec685fSTakashi Iwai new_client->ops->reprobe(new_client->pdev); 2838d608aa6SDave Airlie 2846a9ee8afSDave Airlie if (active->pwr_state == VGA_SWITCHEROO_ON) 2856a9ee8afSDave Airlie vga_switchoff(active); 2866a9ee8afSDave Airlie 2876a9ee8afSDave Airlie new_client->active = true; 2886a9ee8afSDave Airlie return 0; 2896a9ee8afSDave Airlie } 2906a9ee8afSDave Airlie 29179721e0aSTakashi Iwai static bool check_can_switch(void) 29279721e0aSTakashi Iwai { 29379721e0aSTakashi Iwai struct vga_switcheroo_client *client; 29479721e0aSTakashi Iwai 29579721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 29626ec685fSTakashi Iwai if (!client->ops->can_switch(client->pdev)) { 29779721e0aSTakashi Iwai printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id); 29879721e0aSTakashi Iwai return false; 29979721e0aSTakashi Iwai } 30079721e0aSTakashi Iwai } 30179721e0aSTakashi Iwai return true; 30279721e0aSTakashi Iwai } 30379721e0aSTakashi Iwai 3046a9ee8afSDave Airlie static ssize_t 3056a9ee8afSDave Airlie vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, 3066a9ee8afSDave Airlie size_t cnt, loff_t *ppos) 3076a9ee8afSDave Airlie { 3086a9ee8afSDave Airlie char usercmd[64]; 3096a9ee8afSDave Airlie const char *pdev_name; 31079721e0aSTakashi Iwai int ret; 3116a9ee8afSDave Airlie bool delay = false, can_switch; 312851ab954SDave Airlie bool just_mux = false; 3136a9ee8afSDave Airlie int client_id = -1; 3146a9ee8afSDave Airlie struct vga_switcheroo_client *client = NULL; 3156a9ee8afSDave Airlie 3166a9ee8afSDave Airlie if (cnt > 63) 3176a9ee8afSDave Airlie cnt = 63; 3186a9ee8afSDave Airlie 3196a9ee8afSDave Airlie if (copy_from_user(usercmd, ubuf, cnt)) 3206a9ee8afSDave Airlie return -EFAULT; 3216a9ee8afSDave Airlie 3226a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 3236a9ee8afSDave Airlie 3248c88e50bSJiri Slaby if (!vgasr_priv.active) { 3258c88e50bSJiri Slaby cnt = -EINVAL; 3268c88e50bSJiri Slaby goto out; 3278c88e50bSJiri Slaby } 3286a9ee8afSDave Airlie 3296a9ee8afSDave Airlie /* pwr off the device not in use */ 3306a9ee8afSDave Airlie if (strncmp(usercmd, "OFF", 3) == 0) { 33179721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 33279721e0aSTakashi Iwai if (client->active) 3336a9ee8afSDave Airlie continue; 33479721e0aSTakashi Iwai if (client->pwr_state == VGA_SWITCHEROO_ON) 33579721e0aSTakashi Iwai vga_switchoff(client); 3366a9ee8afSDave Airlie } 3376a9ee8afSDave Airlie goto out; 3386a9ee8afSDave Airlie } 3396a9ee8afSDave Airlie /* pwr on the device not in use */ 3406a9ee8afSDave Airlie if (strncmp(usercmd, "ON", 2) == 0) { 34179721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 34279721e0aSTakashi Iwai if (client->active) 3436a9ee8afSDave Airlie continue; 34479721e0aSTakashi Iwai if (client->pwr_state == VGA_SWITCHEROO_OFF) 34579721e0aSTakashi Iwai vga_switchon(client); 3466a9ee8afSDave Airlie } 3476a9ee8afSDave Airlie goto out; 3486a9ee8afSDave Airlie } 3496a9ee8afSDave Airlie 3506a9ee8afSDave Airlie /* request a delayed switch - test can we switch now */ 3516a9ee8afSDave Airlie if (strncmp(usercmd, "DIGD", 4) == 0) { 3526a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_IGD; 3536a9ee8afSDave Airlie delay = true; 3546a9ee8afSDave Airlie } 3556a9ee8afSDave Airlie 3566a9ee8afSDave Airlie if (strncmp(usercmd, "DDIS", 4) == 0) { 3576a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_DIS; 3586a9ee8afSDave Airlie delay = true; 3596a9ee8afSDave Airlie } 3606a9ee8afSDave Airlie 3616a9ee8afSDave Airlie if (strncmp(usercmd, "IGD", 3) == 0) 3626a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_IGD; 3636a9ee8afSDave Airlie 3646a9ee8afSDave Airlie if (strncmp(usercmd, "DIS", 3) == 0) 3656a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_DIS; 3666a9ee8afSDave Airlie 367fea6f330SDan Carpenter if (strncmp(usercmd, "MIGD", 4) == 0) { 368851ab954SDave Airlie just_mux = true; 369851ab954SDave Airlie client_id = VGA_SWITCHEROO_IGD; 370851ab954SDave Airlie } 371fea6f330SDan Carpenter if (strncmp(usercmd, "MDIS", 4) == 0) { 372851ab954SDave Airlie just_mux = true; 373851ab954SDave Airlie client_id = VGA_SWITCHEROO_DIS; 374851ab954SDave Airlie } 375851ab954SDave Airlie 3766a9ee8afSDave Airlie if (client_id == -1) 3776a9ee8afSDave Airlie goto out; 37879721e0aSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, client_id); 37979721e0aSTakashi Iwai if (!client) 38079721e0aSTakashi Iwai goto out; 3816a9ee8afSDave Airlie 3826a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = false; 383851ab954SDave Airlie 384851ab954SDave Airlie if (just_mux) { 385851ab954SDave Airlie ret = vgasr_priv.handler->switchto(client_id); 386851ab954SDave Airlie goto out; 387851ab954SDave Airlie } 388851ab954SDave Airlie 38979721e0aSTakashi Iwai if (client->active) 390a67b8887SFlorian Mickler goto out; 391a67b8887SFlorian Mickler 3926a9ee8afSDave Airlie /* okay we want a switch - test if devices are willing to switch */ 39379721e0aSTakashi Iwai can_switch = check_can_switch(); 3946a9ee8afSDave Airlie 3956a9ee8afSDave Airlie if (can_switch == false && delay == false) 3966a9ee8afSDave Airlie goto out; 3976a9ee8afSDave Airlie 39879721e0aSTakashi Iwai if (can_switch) { 3996a9ee8afSDave Airlie pdev_name = pci_name(client->pdev); 40066b37c67SDave Airlie ret = vga_switchto_stage1(client); 4016a9ee8afSDave Airlie if (ret) 40266b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret); 40366b37c67SDave Airlie 40466b37c67SDave Airlie ret = vga_switchto_stage2(client); 40566b37c67SDave Airlie if (ret) 40666b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: switching failed stage 2 %d\n", ret); 40766b37c67SDave Airlie 4086a9ee8afSDave Airlie } else { 4096a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id); 4106a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = true; 4116a9ee8afSDave Airlie vgasr_priv.delayed_client_id = client_id; 4126a9ee8afSDave Airlie 41366b37c67SDave Airlie ret = vga_switchto_stage1(client); 41466b37c67SDave Airlie if (ret) 41566b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: delayed switching stage 1 failed %d\n", ret); 4166a9ee8afSDave Airlie } 4176a9ee8afSDave Airlie 4186a9ee8afSDave Airlie out: 4196a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 4206a9ee8afSDave Airlie return cnt; 4216a9ee8afSDave Airlie } 4226a9ee8afSDave Airlie 4236a9ee8afSDave Airlie static const struct file_operations vga_switcheroo_debugfs_fops = { 4246a9ee8afSDave Airlie .owner = THIS_MODULE, 4256a9ee8afSDave Airlie .open = vga_switcheroo_debugfs_open, 4266a9ee8afSDave Airlie .write = vga_switcheroo_debugfs_write, 4276a9ee8afSDave Airlie .read = seq_read, 4286a9ee8afSDave Airlie .llseek = seq_lseek, 4296a9ee8afSDave Airlie .release = single_release, 4306a9ee8afSDave Airlie }; 4316a9ee8afSDave Airlie 4326a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv) 4336a9ee8afSDave Airlie { 4346a9ee8afSDave Airlie if (priv->switch_file) { 4356a9ee8afSDave Airlie debugfs_remove(priv->switch_file); 4366a9ee8afSDave Airlie priv->switch_file = NULL; 4376a9ee8afSDave Airlie } 4386a9ee8afSDave Airlie if (priv->debugfs_root) { 4396a9ee8afSDave Airlie debugfs_remove(priv->debugfs_root); 4406a9ee8afSDave Airlie priv->debugfs_root = NULL; 4416a9ee8afSDave Airlie } 4426a9ee8afSDave Airlie } 4436a9ee8afSDave Airlie 4446a9ee8afSDave Airlie static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv) 4456a9ee8afSDave Airlie { 4466a9ee8afSDave Airlie /* already initialised */ 4476a9ee8afSDave Airlie if (priv->debugfs_root) 4486a9ee8afSDave Airlie return 0; 4496a9ee8afSDave Airlie priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL); 4506a9ee8afSDave Airlie 4516a9ee8afSDave Airlie if (!priv->debugfs_root) { 4526a9ee8afSDave Airlie printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n"); 4536a9ee8afSDave Airlie goto fail; 4546a9ee8afSDave Airlie } 4556a9ee8afSDave Airlie 4566a9ee8afSDave Airlie priv->switch_file = debugfs_create_file("switch", 0644, 4576a9ee8afSDave Airlie priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops); 4586a9ee8afSDave Airlie if (!priv->switch_file) { 4596a9ee8afSDave Airlie printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n"); 4606a9ee8afSDave Airlie goto fail; 4616a9ee8afSDave Airlie } 4626a9ee8afSDave Airlie return 0; 4636a9ee8afSDave Airlie fail: 4646a9ee8afSDave Airlie vga_switcheroo_debugfs_fini(priv); 4656a9ee8afSDave Airlie return -1; 4666a9ee8afSDave Airlie } 4676a9ee8afSDave Airlie 4686a9ee8afSDave Airlie int vga_switcheroo_process_delayed_switch(void) 4696a9ee8afSDave Airlie { 47079721e0aSTakashi Iwai struct vga_switcheroo_client *client; 4716a9ee8afSDave Airlie const char *pdev_name; 4726a9ee8afSDave Airlie int ret; 4736a9ee8afSDave Airlie int err = -EINVAL; 4746a9ee8afSDave Airlie 4756a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 4766a9ee8afSDave Airlie if (!vgasr_priv.delayed_switch_active) 4776a9ee8afSDave Airlie goto err; 4786a9ee8afSDave Airlie 4796a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); 4806a9ee8afSDave Airlie 48179721e0aSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, 48279721e0aSTakashi Iwai vgasr_priv.delayed_client_id); 48379721e0aSTakashi Iwai if (!client || !check_can_switch()) 4846a9ee8afSDave Airlie goto err; 4856a9ee8afSDave Airlie 4866a9ee8afSDave Airlie pdev_name = pci_name(client->pdev); 48766b37c67SDave Airlie ret = vga_switchto_stage2(client); 4886a9ee8afSDave Airlie if (ret) 48966b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret); 4906a9ee8afSDave Airlie 4916a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = false; 4926a9ee8afSDave Airlie err = 0; 4936a9ee8afSDave Airlie err: 4946a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 4956a9ee8afSDave Airlie return err; 4966a9ee8afSDave Airlie } 4976a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); 4986a9ee8afSDave Airlie 499