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 603e9e63dbSTakashi Iwai #define ID_BIT_AUDIO 0x100 613e9e63dbSTakashi Iwai #define client_is_audio(c) ((c)->id & ID_BIT_AUDIO) 623e9e63dbSTakashi Iwai #define client_is_vga(c) ((c)->id == -1 || !client_is_audio(c)) 633e9e63dbSTakashi Iwai #define client_id(c) ((c)->id & ~ID_BIT_AUDIO) 643e9e63dbSTakashi Iwai 656a9ee8afSDave Airlie static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); 666a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); 676a9ee8afSDave Airlie 686a9ee8afSDave Airlie /* only one switcheroo per system */ 6979721e0aSTakashi Iwai static struct vgasr_priv vgasr_priv = { 7079721e0aSTakashi Iwai .clients = LIST_HEAD_INIT(vgasr_priv.clients), 7179721e0aSTakashi Iwai }; 726a9ee8afSDave Airlie 736a9ee8afSDave Airlie int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) 746a9ee8afSDave Airlie { 756a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 766a9ee8afSDave Airlie if (vgasr_priv.handler) { 776a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 786a9ee8afSDave Airlie return -EINVAL; 796a9ee8afSDave Airlie } 806a9ee8afSDave Airlie 816a9ee8afSDave Airlie vgasr_priv.handler = handler; 826a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 836a9ee8afSDave Airlie return 0; 846a9ee8afSDave Airlie } 856a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_register_handler); 866a9ee8afSDave Airlie 876a9ee8afSDave Airlie void vga_switcheroo_unregister_handler(void) 886a9ee8afSDave Airlie { 896a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 906a9ee8afSDave Airlie vgasr_priv.handler = NULL; 916a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 926a9ee8afSDave Airlie } 936a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_unregister_handler); 946a9ee8afSDave Airlie 956a9ee8afSDave Airlie static void vga_switcheroo_enable(void) 966a9ee8afSDave Airlie { 976a9ee8afSDave Airlie int ret; 9879721e0aSTakashi Iwai struct vga_switcheroo_client *client; 9979721e0aSTakashi Iwai 1006a9ee8afSDave Airlie /* call the handler to init */ 1016a9ee8afSDave Airlie vgasr_priv.handler->init(); 1026a9ee8afSDave Airlie 10379721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 1043e9e63dbSTakashi Iwai if (client->id != -1) 1053e9e63dbSTakashi Iwai continue; 10679721e0aSTakashi Iwai ret = vgasr_priv.handler->get_client_id(client->pdev); 1076a9ee8afSDave Airlie if (ret < 0) 1086a9ee8afSDave Airlie return; 1096a9ee8afSDave Airlie 11079721e0aSTakashi Iwai client->id = ret; 1116a9ee8afSDave Airlie } 1126a9ee8afSDave Airlie vga_switcheroo_debugfs_init(&vgasr_priv); 1136a9ee8afSDave Airlie vgasr_priv.active = true; 1146a9ee8afSDave Airlie } 1156a9ee8afSDave Airlie 1163e9e63dbSTakashi Iwai static int register_client(struct pci_dev *pdev, 1173e9e63dbSTakashi Iwai const struct vga_switcheroo_client_ops *ops, 1183e9e63dbSTakashi Iwai int id, bool active) 1196a9ee8afSDave Airlie { 12079721e0aSTakashi Iwai struct vga_switcheroo_client *client; 12179721e0aSTakashi Iwai 12279721e0aSTakashi Iwai client = kzalloc(sizeof(*client), GFP_KERNEL); 12379721e0aSTakashi Iwai if (!client) 12479721e0aSTakashi Iwai return -ENOMEM; 12579721e0aSTakashi Iwai 12679721e0aSTakashi Iwai client->pwr_state = VGA_SWITCHEROO_ON; 12779721e0aSTakashi Iwai client->pdev = pdev; 12826ec685fSTakashi Iwai client->ops = ops; 1293e9e63dbSTakashi Iwai client->id = id; 1303e9e63dbSTakashi Iwai client->active = active; 1316a9ee8afSDave Airlie 1326a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 13379721e0aSTakashi Iwai list_add_tail(&client->list, &vgasr_priv.clients); 1343e9e63dbSTakashi Iwai if (client_is_vga(client)) 13579721e0aSTakashi Iwai vgasr_priv.registered_clients++; 1366a9ee8afSDave Airlie 1376a9ee8afSDave Airlie /* if we get two clients + handler */ 1383e9e63dbSTakashi Iwai if (!vgasr_priv.active && 1393e9e63dbSTakashi Iwai vgasr_priv.registered_clients == 2 && vgasr_priv.handler) { 1406a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: enabled\n"); 1416a9ee8afSDave Airlie vga_switcheroo_enable(); 1426a9ee8afSDave Airlie } 1436a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 1446a9ee8afSDave Airlie return 0; 1456a9ee8afSDave Airlie } 1463e9e63dbSTakashi Iwai 1473e9e63dbSTakashi Iwai int vga_switcheroo_register_client(struct pci_dev *pdev, 1483e9e63dbSTakashi Iwai const struct vga_switcheroo_client_ops *ops) 1493e9e63dbSTakashi Iwai { 1503e9e63dbSTakashi Iwai return register_client(pdev, ops, -1, 1513e9e63dbSTakashi Iwai pdev == vga_default_device()); 1523e9e63dbSTakashi Iwai } 1536a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_register_client); 1546a9ee8afSDave Airlie 1553e9e63dbSTakashi Iwai int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 1563e9e63dbSTakashi Iwai const struct vga_switcheroo_client_ops *ops, 1573e9e63dbSTakashi Iwai int id, bool active) 1583e9e63dbSTakashi Iwai { 1593e9e63dbSTakashi Iwai return register_client(pdev, ops, id | ID_BIT_AUDIO, active); 1603e9e63dbSTakashi Iwai } 1613e9e63dbSTakashi Iwai EXPORT_SYMBOL(vga_switcheroo_register_audio_client); 1623e9e63dbSTakashi Iwai 16379721e0aSTakashi Iwai static struct vga_switcheroo_client * 16479721e0aSTakashi Iwai find_client_from_pci(struct list_head *head, struct pci_dev *pdev) 16579721e0aSTakashi Iwai { 16679721e0aSTakashi Iwai struct vga_switcheroo_client *client; 16779721e0aSTakashi Iwai list_for_each_entry(client, head, list) 16879721e0aSTakashi Iwai if (client->pdev == pdev) 16979721e0aSTakashi Iwai return client; 17079721e0aSTakashi Iwai return NULL; 17179721e0aSTakashi Iwai } 17279721e0aSTakashi Iwai 17379721e0aSTakashi Iwai static struct vga_switcheroo_client * 17479721e0aSTakashi Iwai find_client_from_id(struct list_head *head, int client_id) 17579721e0aSTakashi Iwai { 17679721e0aSTakashi Iwai struct vga_switcheroo_client *client; 17779721e0aSTakashi Iwai list_for_each_entry(client, head, list) 17879721e0aSTakashi Iwai if (client->id == client_id) 17979721e0aSTakashi Iwai return client; 18079721e0aSTakashi Iwai return NULL; 18179721e0aSTakashi Iwai } 18279721e0aSTakashi Iwai 18379721e0aSTakashi Iwai static struct vga_switcheroo_client * 18479721e0aSTakashi Iwai find_active_client(struct list_head *head) 18579721e0aSTakashi Iwai { 18679721e0aSTakashi Iwai struct vga_switcheroo_client *client; 18779721e0aSTakashi Iwai list_for_each_entry(client, head, list) 1883e9e63dbSTakashi Iwai if (client->active && client_is_vga(client)) 18979721e0aSTakashi Iwai return client; 19079721e0aSTakashi Iwai return NULL; 19179721e0aSTakashi Iwai } 19279721e0aSTakashi Iwai 1936a9ee8afSDave Airlie void vga_switcheroo_unregister_client(struct pci_dev *pdev) 1946a9ee8afSDave Airlie { 19579721e0aSTakashi Iwai struct vga_switcheroo_client *client; 1966a9ee8afSDave Airlie 1976a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 19879721e0aSTakashi Iwai client = find_client_from_pci(&vgasr_priv.clients, pdev); 19979721e0aSTakashi Iwai if (client) { 2003e9e63dbSTakashi Iwai if (client_is_vga(client)) 2013e9e63dbSTakashi Iwai vgasr_priv.registered_clients--; 20279721e0aSTakashi Iwai list_del(&client->list); 20379721e0aSTakashi Iwai kfree(client); 2046a9ee8afSDave Airlie } 2053e9e63dbSTakashi Iwai if (vgasr_priv.active && vgasr_priv.registered_clients < 2) { 2066a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: disabled\n"); 2076a9ee8afSDave Airlie vga_switcheroo_debugfs_fini(&vgasr_priv); 2086a9ee8afSDave Airlie vgasr_priv.active = false; 2093e9e63dbSTakashi Iwai } 2106a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 2116a9ee8afSDave Airlie } 2126a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_unregister_client); 2136a9ee8afSDave Airlie 2146a9ee8afSDave Airlie void vga_switcheroo_client_fb_set(struct pci_dev *pdev, 2156a9ee8afSDave Airlie struct fb_info *info) 2166a9ee8afSDave Airlie { 21779721e0aSTakashi Iwai struct vga_switcheroo_client *client; 2186a9ee8afSDave Airlie 2196a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 22079721e0aSTakashi Iwai client = find_client_from_pci(&vgasr_priv.clients, pdev); 22179721e0aSTakashi Iwai if (client) 22279721e0aSTakashi Iwai client->fb_info = info; 2236a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 2246a9ee8afSDave Airlie } 2256a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_client_fb_set); 2266a9ee8afSDave Airlie 2276a9ee8afSDave Airlie static int vga_switcheroo_show(struct seq_file *m, void *v) 2286a9ee8afSDave Airlie { 22979721e0aSTakashi Iwai struct vga_switcheroo_client *client; 23079721e0aSTakashi Iwai int i = 0; 2316a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 23279721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 2333e9e63dbSTakashi Iwai seq_printf(m, "%d:%s%s:%c:%s:%s\n", i, 2343e9e63dbSTakashi Iwai client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", 2353e9e63dbSTakashi Iwai client_is_vga(client) ? "" : "-Audio", 23679721e0aSTakashi Iwai client->active ? '+' : ' ', 23779721e0aSTakashi Iwai client->pwr_state ? "Pwr" : "Off", 23879721e0aSTakashi Iwai pci_name(client->pdev)); 23979721e0aSTakashi Iwai i++; 2406a9ee8afSDave Airlie } 2416a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 2426a9ee8afSDave Airlie return 0; 2436a9ee8afSDave Airlie } 2446a9ee8afSDave Airlie 2456a9ee8afSDave Airlie static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file) 2466a9ee8afSDave Airlie { 2476a9ee8afSDave Airlie return single_open(file, vga_switcheroo_show, NULL); 2486a9ee8afSDave Airlie } 2496a9ee8afSDave Airlie 2506a9ee8afSDave Airlie static int vga_switchon(struct vga_switcheroo_client *client) 2516a9ee8afSDave Airlie { 2525cfb3c3aSDave Airlie if (vgasr_priv.handler->power_state) 2535cfb3c3aSDave Airlie vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); 2546a9ee8afSDave Airlie /* call the driver callback to turn on device */ 25526ec685fSTakashi Iwai client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); 2566a9ee8afSDave Airlie client->pwr_state = VGA_SWITCHEROO_ON; 2576a9ee8afSDave Airlie return 0; 2586a9ee8afSDave Airlie } 2596a9ee8afSDave Airlie 2606a9ee8afSDave Airlie static int vga_switchoff(struct vga_switcheroo_client *client) 2616a9ee8afSDave Airlie { 2626a9ee8afSDave Airlie /* call the driver callback to turn off device */ 26326ec685fSTakashi Iwai client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); 2645cfb3c3aSDave Airlie if (vgasr_priv.handler->power_state) 2656a9ee8afSDave Airlie vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); 2666a9ee8afSDave Airlie client->pwr_state = VGA_SWITCHEROO_OFF; 2676a9ee8afSDave Airlie return 0; 2686a9ee8afSDave Airlie } 2696a9ee8afSDave Airlie 2703e9e63dbSTakashi Iwai static void set_audio_state(int id, int state) 2713e9e63dbSTakashi Iwai { 2723e9e63dbSTakashi Iwai struct vga_switcheroo_client *client; 2733e9e63dbSTakashi Iwai 2743e9e63dbSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO); 2753e9e63dbSTakashi Iwai if (client && client->pwr_state != state) { 2763e9e63dbSTakashi Iwai client->ops->set_gpu_state(client->pdev, state); 2773e9e63dbSTakashi Iwai client->pwr_state = state; 2783e9e63dbSTakashi Iwai } 2793e9e63dbSTakashi Iwai } 2803e9e63dbSTakashi Iwai 28166b37c67SDave Airlie /* stage one happens before delay */ 28266b37c67SDave Airlie static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) 2836a9ee8afSDave Airlie { 28479721e0aSTakashi Iwai struct vga_switcheroo_client *active; 2856a9ee8afSDave Airlie 28679721e0aSTakashi Iwai active = find_active_client(&vgasr_priv.clients); 2876a9ee8afSDave Airlie if (!active) 2886a9ee8afSDave Airlie return 0; 2896a9ee8afSDave Airlie 2906a9ee8afSDave Airlie if (new_client->pwr_state == VGA_SWITCHEROO_OFF) 2916a9ee8afSDave Airlie vga_switchon(new_client); 2926a9ee8afSDave Airlie 2932fbe8c7cSMatthew Garrett vga_set_default_device(new_client->pdev); 2943e9e63dbSTakashi Iwai set_audio_state(new_client->id, VGA_SWITCHEROO_ON); 2952fbe8c7cSMatthew Garrett 29666b37c67SDave Airlie return 0; 29766b37c67SDave Airlie } 29866b37c67SDave Airlie 29966b37c67SDave Airlie /* post delay */ 30066b37c67SDave Airlie static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) 30166b37c67SDave Airlie { 30266b37c67SDave Airlie int ret; 30379721e0aSTakashi Iwai struct vga_switcheroo_client *active; 30466b37c67SDave Airlie 30579721e0aSTakashi Iwai active = find_active_client(&vgasr_priv.clients); 30666b37c67SDave Airlie if (!active) 30766b37c67SDave Airlie return 0; 30866b37c67SDave Airlie 30966b37c67SDave Airlie active->active = false; 3106a9ee8afSDave Airlie 3116a9ee8afSDave Airlie if (new_client->fb_info) { 3126a9ee8afSDave Airlie struct fb_event event; 3136a9ee8afSDave Airlie event.info = new_client->fb_info; 3146a9ee8afSDave Airlie fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); 3156a9ee8afSDave Airlie } 3166a9ee8afSDave Airlie 3176a9ee8afSDave Airlie ret = vgasr_priv.handler->switchto(new_client->id); 3186a9ee8afSDave Airlie if (ret) 3196a9ee8afSDave Airlie return ret; 3206a9ee8afSDave Airlie 32126ec685fSTakashi Iwai if (new_client->ops->reprobe) 32226ec685fSTakashi Iwai new_client->ops->reprobe(new_client->pdev); 3238d608aa6SDave Airlie 3243e9e63dbSTakashi Iwai set_audio_state(active->id, VGA_SWITCHEROO_OFF); 3253e9e63dbSTakashi Iwai 3266a9ee8afSDave Airlie if (active->pwr_state == VGA_SWITCHEROO_ON) 3276a9ee8afSDave Airlie vga_switchoff(active); 3286a9ee8afSDave Airlie 3296a9ee8afSDave Airlie new_client->active = true; 3306a9ee8afSDave Airlie return 0; 3316a9ee8afSDave Airlie } 3326a9ee8afSDave Airlie 33379721e0aSTakashi Iwai static bool check_can_switch(void) 33479721e0aSTakashi Iwai { 33579721e0aSTakashi Iwai struct vga_switcheroo_client *client; 33679721e0aSTakashi Iwai 33779721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 33826ec685fSTakashi Iwai if (!client->ops->can_switch(client->pdev)) { 33979721e0aSTakashi Iwai printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id); 34079721e0aSTakashi Iwai return false; 34179721e0aSTakashi Iwai } 34279721e0aSTakashi Iwai } 34379721e0aSTakashi Iwai return true; 34479721e0aSTakashi Iwai } 34579721e0aSTakashi Iwai 3466a9ee8afSDave Airlie static ssize_t 3476a9ee8afSDave Airlie vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, 3486a9ee8afSDave Airlie size_t cnt, loff_t *ppos) 3496a9ee8afSDave Airlie { 3506a9ee8afSDave Airlie char usercmd[64]; 3516a9ee8afSDave Airlie const char *pdev_name; 35279721e0aSTakashi Iwai int ret; 3536a9ee8afSDave Airlie bool delay = false, can_switch; 354851ab954SDave Airlie bool just_mux = false; 3556a9ee8afSDave Airlie int client_id = -1; 3566a9ee8afSDave Airlie struct vga_switcheroo_client *client = NULL; 3576a9ee8afSDave Airlie 3586a9ee8afSDave Airlie if (cnt > 63) 3596a9ee8afSDave Airlie cnt = 63; 3606a9ee8afSDave Airlie 3616a9ee8afSDave Airlie if (copy_from_user(usercmd, ubuf, cnt)) 3626a9ee8afSDave Airlie return -EFAULT; 3636a9ee8afSDave Airlie 3646a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 3656a9ee8afSDave Airlie 3668c88e50bSJiri Slaby if (!vgasr_priv.active) { 3678c88e50bSJiri Slaby cnt = -EINVAL; 3688c88e50bSJiri Slaby goto out; 3698c88e50bSJiri Slaby } 3706a9ee8afSDave Airlie 3716a9ee8afSDave Airlie /* pwr off the device not in use */ 3726a9ee8afSDave Airlie if (strncmp(usercmd, "OFF", 3) == 0) { 37379721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 37479721e0aSTakashi Iwai if (client->active) 3756a9ee8afSDave Airlie continue; 37679721e0aSTakashi Iwai if (client->pwr_state == VGA_SWITCHEROO_ON) 37779721e0aSTakashi Iwai vga_switchoff(client); 3786a9ee8afSDave Airlie } 3796a9ee8afSDave Airlie goto out; 3806a9ee8afSDave Airlie } 3816a9ee8afSDave Airlie /* pwr on the device not in use */ 3826a9ee8afSDave Airlie if (strncmp(usercmd, "ON", 2) == 0) { 38379721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 38479721e0aSTakashi Iwai if (client->active) 3856a9ee8afSDave Airlie continue; 38679721e0aSTakashi Iwai if (client->pwr_state == VGA_SWITCHEROO_OFF) 38779721e0aSTakashi Iwai vga_switchon(client); 3886a9ee8afSDave Airlie } 3896a9ee8afSDave Airlie goto out; 3906a9ee8afSDave Airlie } 3916a9ee8afSDave Airlie 3926a9ee8afSDave Airlie /* request a delayed switch - test can we switch now */ 3936a9ee8afSDave Airlie if (strncmp(usercmd, "DIGD", 4) == 0) { 3946a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_IGD; 3956a9ee8afSDave Airlie delay = true; 3966a9ee8afSDave Airlie } 3976a9ee8afSDave Airlie 3986a9ee8afSDave Airlie if (strncmp(usercmd, "DDIS", 4) == 0) { 3996a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_DIS; 4006a9ee8afSDave Airlie delay = true; 4016a9ee8afSDave Airlie } 4026a9ee8afSDave Airlie 4036a9ee8afSDave Airlie if (strncmp(usercmd, "IGD", 3) == 0) 4046a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_IGD; 4056a9ee8afSDave Airlie 4066a9ee8afSDave Airlie if (strncmp(usercmd, "DIS", 3) == 0) 4076a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_DIS; 4086a9ee8afSDave Airlie 409fea6f330SDan Carpenter if (strncmp(usercmd, "MIGD", 4) == 0) { 410851ab954SDave Airlie just_mux = true; 411851ab954SDave Airlie client_id = VGA_SWITCHEROO_IGD; 412851ab954SDave Airlie } 413fea6f330SDan Carpenter if (strncmp(usercmd, "MDIS", 4) == 0) { 414851ab954SDave Airlie just_mux = true; 415851ab954SDave Airlie client_id = VGA_SWITCHEROO_DIS; 416851ab954SDave Airlie } 417851ab954SDave Airlie 4186a9ee8afSDave Airlie if (client_id == -1) 4196a9ee8afSDave Airlie goto out; 42079721e0aSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, client_id); 42179721e0aSTakashi Iwai if (!client) 42279721e0aSTakashi Iwai goto out; 4236a9ee8afSDave Airlie 4246a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = false; 425851ab954SDave Airlie 426851ab954SDave Airlie if (just_mux) { 427851ab954SDave Airlie ret = vgasr_priv.handler->switchto(client_id); 428851ab954SDave Airlie goto out; 429851ab954SDave Airlie } 430851ab954SDave Airlie 43179721e0aSTakashi Iwai if (client->active) 432a67b8887SFlorian Mickler goto out; 433a67b8887SFlorian Mickler 4346a9ee8afSDave Airlie /* okay we want a switch - test if devices are willing to switch */ 43579721e0aSTakashi Iwai can_switch = check_can_switch(); 4366a9ee8afSDave Airlie 4376a9ee8afSDave Airlie if (can_switch == false && delay == false) 4386a9ee8afSDave Airlie goto out; 4396a9ee8afSDave Airlie 44079721e0aSTakashi Iwai if (can_switch) { 4416a9ee8afSDave Airlie pdev_name = pci_name(client->pdev); 44266b37c67SDave Airlie ret = vga_switchto_stage1(client); 4436a9ee8afSDave Airlie if (ret) 44466b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret); 44566b37c67SDave Airlie 44666b37c67SDave Airlie ret = vga_switchto_stage2(client); 44766b37c67SDave Airlie if (ret) 44866b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: switching failed stage 2 %d\n", ret); 44966b37c67SDave Airlie 4506a9ee8afSDave Airlie } else { 4516a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id); 4526a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = true; 4536a9ee8afSDave Airlie vgasr_priv.delayed_client_id = client_id; 4546a9ee8afSDave Airlie 45566b37c67SDave Airlie ret = vga_switchto_stage1(client); 45666b37c67SDave Airlie if (ret) 45766b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: delayed switching stage 1 failed %d\n", ret); 4586a9ee8afSDave Airlie } 4596a9ee8afSDave Airlie 4606a9ee8afSDave Airlie out: 4616a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 4626a9ee8afSDave Airlie return cnt; 4636a9ee8afSDave Airlie } 4646a9ee8afSDave Airlie 4656a9ee8afSDave Airlie static const struct file_operations vga_switcheroo_debugfs_fops = { 4666a9ee8afSDave Airlie .owner = THIS_MODULE, 4676a9ee8afSDave Airlie .open = vga_switcheroo_debugfs_open, 4686a9ee8afSDave Airlie .write = vga_switcheroo_debugfs_write, 4696a9ee8afSDave Airlie .read = seq_read, 4706a9ee8afSDave Airlie .llseek = seq_lseek, 4716a9ee8afSDave Airlie .release = single_release, 4726a9ee8afSDave Airlie }; 4736a9ee8afSDave Airlie 4746a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv) 4756a9ee8afSDave Airlie { 4766a9ee8afSDave Airlie if (priv->switch_file) { 4776a9ee8afSDave Airlie debugfs_remove(priv->switch_file); 4786a9ee8afSDave Airlie priv->switch_file = NULL; 4796a9ee8afSDave Airlie } 4806a9ee8afSDave Airlie if (priv->debugfs_root) { 4816a9ee8afSDave Airlie debugfs_remove(priv->debugfs_root); 4826a9ee8afSDave Airlie priv->debugfs_root = NULL; 4836a9ee8afSDave Airlie } 4846a9ee8afSDave Airlie } 4856a9ee8afSDave Airlie 4866a9ee8afSDave Airlie static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv) 4876a9ee8afSDave Airlie { 4886a9ee8afSDave Airlie /* already initialised */ 4896a9ee8afSDave Airlie if (priv->debugfs_root) 4906a9ee8afSDave Airlie return 0; 4916a9ee8afSDave Airlie priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL); 4926a9ee8afSDave Airlie 4936a9ee8afSDave Airlie if (!priv->debugfs_root) { 4946a9ee8afSDave Airlie printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n"); 4956a9ee8afSDave Airlie goto fail; 4966a9ee8afSDave Airlie } 4976a9ee8afSDave Airlie 4986a9ee8afSDave Airlie priv->switch_file = debugfs_create_file("switch", 0644, 4996a9ee8afSDave Airlie priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops); 5006a9ee8afSDave Airlie if (!priv->switch_file) { 5016a9ee8afSDave Airlie printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n"); 5026a9ee8afSDave Airlie goto fail; 5036a9ee8afSDave Airlie } 5046a9ee8afSDave Airlie return 0; 5056a9ee8afSDave Airlie fail: 5066a9ee8afSDave Airlie vga_switcheroo_debugfs_fini(priv); 5076a9ee8afSDave Airlie return -1; 5086a9ee8afSDave Airlie } 5096a9ee8afSDave Airlie 5106a9ee8afSDave Airlie int vga_switcheroo_process_delayed_switch(void) 5116a9ee8afSDave Airlie { 51279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 5136a9ee8afSDave Airlie const char *pdev_name; 5146a9ee8afSDave Airlie int ret; 5156a9ee8afSDave Airlie int err = -EINVAL; 5166a9ee8afSDave Airlie 5176a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 5186a9ee8afSDave Airlie if (!vgasr_priv.delayed_switch_active) 5196a9ee8afSDave Airlie goto err; 5206a9ee8afSDave Airlie 5216a9ee8afSDave Airlie printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); 5226a9ee8afSDave Airlie 52379721e0aSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, 52479721e0aSTakashi Iwai vgasr_priv.delayed_client_id); 52579721e0aSTakashi Iwai if (!client || !check_can_switch()) 5266a9ee8afSDave Airlie goto err; 5276a9ee8afSDave Airlie 5286a9ee8afSDave Airlie pdev_name = pci_name(client->pdev); 52966b37c67SDave Airlie ret = vga_switchto_stage2(client); 5306a9ee8afSDave Airlie if (ret) 53166b37c67SDave Airlie printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret); 5326a9ee8afSDave Airlie 5336a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = false; 5346a9ee8afSDave Airlie err = 0; 5356a9ee8afSDave Airlie err: 5366a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 5376a9ee8afSDave Airlie return err; 5386a9ee8afSDave Airlie } 5396a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); 5406a9ee8afSDave Airlie 541