xref: /openbmc/linux/drivers/gpu/vga/vga_switcheroo.c (revision 3e9e63db)
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