xref: /openbmc/linux/drivers/gpu/vga/vga_switcheroo.c (revision 99efde6c)
16a9ee8afSDave Airlie /*
2a645654bSLukas Wunner  * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs
3a645654bSLukas Wunner  *
46a9ee8afSDave Airlie  * Copyright (c) 2010 Red Hat Inc.
56a9ee8afSDave Airlie  * Author : Dave Airlie <airlied@redhat.com>
66a9ee8afSDave Airlie  *
7a645654bSLukas Wunner  * Copyright (c) 2015 Lukas Wunner <lukas@wunner.de>
86a9ee8afSDave Airlie  *
9a645654bSLukas Wunner  * Permission is hereby granted, free of charge, to any person obtaining a
10a645654bSLukas Wunner  * copy of this software and associated documentation files (the "Software"),
11a645654bSLukas Wunner  * to deal in the Software without restriction, including without limitation
12a645654bSLukas Wunner  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13a645654bSLukas Wunner  * and/or sell copies of the Software, and to permit persons to whom the
14a645654bSLukas Wunner  * Software is furnished to do so, subject to the following conditions:
156a9ee8afSDave Airlie  *
16a645654bSLukas Wunner  * The above copyright notice and this permission notice (including the next
17a645654bSLukas Wunner  * paragraph) shall be included in all copies or substantial portions of the
18a645654bSLukas Wunner  * Software.
1971309278SThierry Reding  *
20a645654bSLukas Wunner  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21a645654bSLukas Wunner  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22a645654bSLukas Wunner  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23a645654bSLukas Wunner  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24a645654bSLukas Wunner  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25a645654bSLukas Wunner  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26a645654bSLukas Wunner  * DEALINGS
27a645654bSLukas Wunner  * IN THE SOFTWARE.
2871309278SThierry Reding  *
296a9ee8afSDave Airlie  */
306a9ee8afSDave Airlie 
319b0be1ebSThierry Reding #define pr_fmt(fmt) "vga_switcheroo: " fmt
329b0be1ebSThierry Reding 
33b00e5334SLukas Wunner #include <linux/apple-gmux.h>
344127838cSLukas Wunner #include <linux/console.h>
356a9ee8afSDave Airlie #include <linux/debugfs.h>
366a9ee8afSDave Airlie #include <linux/fb.h>
374127838cSLukas Wunner #include <linux/fs.h>
381cd51b5dSDaniel Vetter #include <linux/fbcon.h>
394127838cSLukas Wunner #include <linux/module.h>
406a9ee8afSDave Airlie #include <linux/pci.h>
41989561deSTomeu Vizoso #include <linux/pm_domain.h>
420d69704aSDave Airlie #include <linux/pm_runtime.h>
434127838cSLukas Wunner #include <linux/seq_file.h>
444127838cSLukas Wunner #include <linux/uaccess.h>
452fbe8c7cSMatthew Garrett #include <linux/vgaarb.h>
464127838cSLukas Wunner #include <linux/vga_switcheroo.h>
472fbe8c7cSMatthew Garrett 
48a645654bSLukas Wunner /**
49a645654bSLukas Wunner  * DOC: Overview
50a645654bSLukas Wunner  *
51a645654bSLukas Wunner  * vga_switcheroo is the Linux subsystem for laptop hybrid graphics.
52a645654bSLukas Wunner  * These come in two flavors:
53a645654bSLukas Wunner  *
54a645654bSLukas Wunner  * * muxed: Dual GPUs with a multiplexer chip to switch outputs between GPUs.
55a645654bSLukas Wunner  * * muxless: Dual GPUs but only one of them is connected to outputs.
56a645654bSLukas Wunner  *   The other one is merely used to offload rendering, its results
57a645654bSLukas Wunner  *   are copied over PCIe into the framebuffer. On Linux this is
58a645654bSLukas Wunner  *   supported with DRI PRIME.
59a645654bSLukas Wunner  *
60a645654bSLukas Wunner  * Hybrid graphics started to appear in the late Naughties and were initially
61a645654bSLukas Wunner  * all muxed. Newer laptops moved to a muxless architecture for cost reasons.
62a645654bSLukas Wunner  * A notable exception is the MacBook Pro which continues to use a mux.
63a645654bSLukas Wunner  * Muxes come with varying capabilities: Some switch only the panel, others
64a645654bSLukas Wunner  * can also switch external displays. Some switch all display pins at once
65a645654bSLukas Wunner  * while others can switch just the DDC lines. (To allow EDID probing
66a645654bSLukas Wunner  * for the inactive GPU.) Also, muxes are often used to cut power to the
67a645654bSLukas Wunner  * discrete GPU while it is not used.
68a645654bSLukas Wunner  *
693a848662SLukas Wunner  * DRM drivers register GPUs with vga_switcheroo, these are henceforth called
70a645654bSLukas Wunner  * clients. The mux is called the handler. Muxless machines also register a
71a645654bSLukas Wunner  * handler to control the power state of the discrete GPU, its ->switchto
72a645654bSLukas Wunner  * callback is a no-op for obvious reasons. The discrete GPU is often equipped
73a645654bSLukas Wunner  * with an HDA controller for the HDMI/DP audio signal, this will also
74a645654bSLukas Wunner  * register as a client so that vga_switcheroo can take care of the correct
75a645654bSLukas Wunner  * suspend/resume order when changing the discrete GPU's power state. In total
76a645654bSLukas Wunner  * there can thus be up to three clients: Two vga clients (GPUs) and one audio
77a645654bSLukas Wunner  * client (on the discrete GPU). The code is mostly prepared to support
78a645654bSLukas Wunner  * machines with more than two GPUs should they become available.
79e4cb81d7SLukas Wunner  *
80a645654bSLukas Wunner  * The GPU to which the outputs are currently switched is called the
81a645654bSLukas Wunner  * active client in vga_switcheroo parlance. The GPU not in use is the
82e4cb81d7SLukas Wunner  * inactive client. When the inactive client's DRM driver is loaded,
83e4cb81d7SLukas Wunner  * it will be unable to probe the panel's EDID and hence depends on
84e4cb81d7SLukas Wunner  * VBIOS to provide its display modes. If the VBIOS modes are bogus or
85e4cb81d7SLukas Wunner  * if there is no VBIOS at all (which is common on the MacBook Pro),
86e4cb81d7SLukas Wunner  * a client may alternatively request that the DDC lines are temporarily
87e4cb81d7SLukas Wunner  * switched to it, provided that the handler supports this. Switching
88e4cb81d7SLukas Wunner  * only the DDC lines and not the entire output avoids unnecessary
89e4cb81d7SLukas Wunner  * flickering.
90a645654bSLukas Wunner  */
91a645654bSLukas Wunner 
92a645654bSLukas Wunner /**
93a645654bSLukas Wunner  * struct vga_switcheroo_client - registered client
94a645654bSLukas Wunner  * @pdev: client pci device
95a645654bSLukas Wunner  * @fb_info: framebuffer to which console is remapped on switching
968948ca1aSLukas Wunner  * @pwr_state: current power state if manual power control is used.
978948ca1aSLukas Wunner  *	For driver power control, call vga_switcheroo_pwr_state().
98a645654bSLukas Wunner  * @ops: client callbacks
9921c5ba8cSLukas Wunner  * @id: client identifier. Determining the id requires the handler,
10021c5ba8cSLukas Wunner  *	so gpus are initially assigned VGA_SWITCHEROO_UNKNOWN_ID
10121c5ba8cSLukas Wunner  *	and later given their true id in vga_switcheroo_enable()
102a645654bSLukas Wunner  * @active: whether the outputs are currently switched to this client
103a645654bSLukas Wunner  * @driver_power_control: whether power state is controlled by the driver's
104a645654bSLukas Wunner  *	runtime pm. If true, writing ON and OFF to the vga_switcheroo debugfs
105a645654bSLukas Wunner  *	interface is a no-op so as not to interfere with runtime pm
106a645654bSLukas Wunner  * @list: client list
1074aaf448fSJim Qu  * @vga_dev: pci device, indicate which GPU is bound to current audio client
108a645654bSLukas Wunner  *
109a645654bSLukas Wunner  * Registered client. A client can be either a GPU or an audio device on a GPU.
1104aaf448fSJim Qu  * For audio clients, the @fb_info and @active members are bogus. For GPU
1114aaf448fSJim Qu  * clients, the @vga_dev is bogus.
112a645654bSLukas Wunner  */
1136a9ee8afSDave Airlie struct vga_switcheroo_client {
1146a9ee8afSDave Airlie 	struct pci_dev *pdev;
1156a9ee8afSDave Airlie 	struct fb_info *fb_info;
116203d027dSLukas Wunner 	enum vga_switcheroo_state pwr_state;
11726ec685fSTakashi Iwai 	const struct vga_switcheroo_client_ops *ops;
118fa3e967fSLukas Wunner 	enum vga_switcheroo_client_id id;
1196a9ee8afSDave Airlie 	bool active;
1200d69704aSDave Airlie 	bool driver_power_control;
12179721e0aSTakashi Iwai 	struct list_head list;
1224aaf448fSJim Qu 	struct pci_dev *vga_dev;
1236a9ee8afSDave Airlie };
1246a9ee8afSDave Airlie 
125a645654bSLukas Wunner /*
126a645654bSLukas Wunner  * protects access to struct vgasr_priv
127a645654bSLukas Wunner  */
1286a9ee8afSDave Airlie static DEFINE_MUTEX(vgasr_mutex);
1296a9ee8afSDave Airlie 
130a645654bSLukas Wunner /**
131a645654bSLukas Wunner  * struct vgasr_priv - vga_switcheroo private data
132a645654bSLukas Wunner  * @active: whether vga_switcheroo is enabled.
133a645654bSLukas Wunner  *	Prerequisite is the registration of two GPUs and a handler
134a645654bSLukas Wunner  * @delayed_switch_active: whether a delayed switch is pending
135a645654bSLukas Wunner  * @delayed_client_id: client to which a delayed switch is pending
136a645654bSLukas Wunner  * @debugfs_root: directory for vga_switcheroo debugfs interface
137a645654bSLukas Wunner  * @registered_clients: number of registered GPUs
138a645654bSLukas Wunner  *	(counting only vga clients, not audio clients)
139a645654bSLukas Wunner  * @clients: list of registered clients
140a645654bSLukas Wunner  * @handler: registered handler
141156d7d41SLukas Wunner  * @handler_flags: flags of registered handler
142e4cb81d7SLukas Wunner  * @mux_hw_lock: protects mux state
143e4cb81d7SLukas Wunner  *	(in particular while DDC lines are temporarily switched)
144e4cb81d7SLukas Wunner  * @old_ddc_owner: client to which DDC lines will be switched back on unlock
145a645654bSLukas Wunner  *
146a645654bSLukas Wunner  * vga_switcheroo private data. Currently only one vga_switcheroo instance
147a645654bSLukas Wunner  * per system is supported.
148a645654bSLukas Wunner  */
1496a9ee8afSDave Airlie struct vgasr_priv {
1506a9ee8afSDave Airlie 	bool active;
1516a9ee8afSDave Airlie 	bool delayed_switch_active;
1526a9ee8afSDave Airlie 	enum vga_switcheroo_client_id delayed_client_id;
1536a9ee8afSDave Airlie 
1546a9ee8afSDave Airlie 	struct dentry *debugfs_root;
1556a9ee8afSDave Airlie 
1566a9ee8afSDave Airlie 	int registered_clients;
15779721e0aSTakashi Iwai 	struct list_head clients;
1586a9ee8afSDave Airlie 
1595d170139SLukas Wunner 	const struct vga_switcheroo_handler *handler;
160156d7d41SLukas Wunner 	enum vga_switcheroo_handler_flags_t handler_flags;
161e4cb81d7SLukas Wunner 	struct mutex mux_hw_lock;
162e4cb81d7SLukas Wunner 	int old_ddc_owner;
1636a9ee8afSDave Airlie };
1646a9ee8afSDave Airlie 
1653e9e63dbSTakashi Iwai #define ID_BIT_AUDIO		0x100
1663e9e63dbSTakashi Iwai #define client_is_audio(c)		((c)->id & ID_BIT_AUDIO)
1674aaf448fSJim Qu #define client_is_vga(c)		(!client_is_audio(c))
1683e9e63dbSTakashi Iwai #define client_id(c)		((c)->id & ~ID_BIT_AUDIO)
1693e9e63dbSTakashi Iwai 
17090e73491SGreg Kroah-Hartman static void vga_switcheroo_debugfs_init(struct vgasr_priv *priv);
1716a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);
1726a9ee8afSDave Airlie 
1736a9ee8afSDave Airlie /* only one switcheroo per system */
17479721e0aSTakashi Iwai static struct vgasr_priv vgasr_priv = {
17579721e0aSTakashi Iwai 	.clients = LIST_HEAD_INIT(vgasr_priv.clients),
176e4cb81d7SLukas Wunner 	.mux_hw_lock = __MUTEX_INITIALIZER(vgasr_priv.mux_hw_lock),
17779721e0aSTakashi Iwai };
1786a9ee8afSDave Airlie 
vga_switcheroo_ready(void)17936704c0cSSeth Forshee static bool vga_switcheroo_ready(void)
1806a9ee8afSDave Airlie {
18136704c0cSSeth Forshee 	/* we're ready if we get two clients + handler */
18236704c0cSSeth Forshee 	return !vgasr_priv.active &&
18336704c0cSSeth Forshee 	       vgasr_priv.registered_clients == 2 && vgasr_priv.handler;
1846a9ee8afSDave Airlie }
1856a9ee8afSDave Airlie 
vga_switcheroo_enable(void)1866a9ee8afSDave Airlie static void vga_switcheroo_enable(void)
1876a9ee8afSDave Airlie {
1886a9ee8afSDave Airlie 	int ret;
18979721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
19079721e0aSTakashi Iwai 
1916a9ee8afSDave Airlie 	/* call the handler to init */
192e99eac5eSSeth Forshee 	if (vgasr_priv.handler->init)
1936a9ee8afSDave Airlie 		vgasr_priv.handler->init();
1946a9ee8afSDave Airlie 
19579721e0aSTakashi Iwai 	list_for_each_entry(client, &vgasr_priv.clients, list) {
1964aaf448fSJim Qu 		if (!client_is_vga(client) ||
1974aaf448fSJim Qu 		     client_id(client) != VGA_SWITCHEROO_UNKNOWN_ID)
1983e9e63dbSTakashi Iwai 			continue;
1994aaf448fSJim Qu 
20079721e0aSTakashi Iwai 		ret = vgasr_priv.handler->get_client_id(client->pdev);
2016a9ee8afSDave Airlie 		if (ret < 0)
2026a9ee8afSDave Airlie 			return;
2036a9ee8afSDave Airlie 
20479721e0aSTakashi Iwai 		client->id = ret;
2056a9ee8afSDave Airlie 	}
2064aaf448fSJim Qu 
2074aaf448fSJim Qu 	list_for_each_entry(client, &vgasr_priv.clients, list) {
2084aaf448fSJim Qu 		if (!client_is_audio(client) ||
2094aaf448fSJim Qu 		     client_id(client) != VGA_SWITCHEROO_UNKNOWN_ID)
2104aaf448fSJim Qu 			continue;
2114aaf448fSJim Qu 
2124aaf448fSJim Qu 		ret = vgasr_priv.handler->get_client_id(client->vga_dev);
2134aaf448fSJim Qu 		if (ret < 0)
2144aaf448fSJim Qu 			return;
2154aaf448fSJim Qu 
2164aaf448fSJim Qu 		client->id = ret | ID_BIT_AUDIO;
21737a3a98eSTakashi Iwai 		if (client->ops->gpu_bound)
21837a3a98eSTakashi Iwai 			client->ops->gpu_bound(client->pdev, ret);
2194aaf448fSJim Qu 	}
2204aaf448fSJim Qu 
2216a9ee8afSDave Airlie 	vga_switcheroo_debugfs_init(&vgasr_priv);
2226a9ee8afSDave Airlie 	vgasr_priv.active = true;
2236a9ee8afSDave Airlie }
2246a9ee8afSDave Airlie 
225a645654bSLukas Wunner /**
226a645654bSLukas Wunner  * vga_switcheroo_register_handler() - register handler
227a645654bSLukas Wunner  * @handler: handler callbacks
228156d7d41SLukas Wunner  * @handler_flags: handler flags
229a645654bSLukas Wunner  *
230a645654bSLukas Wunner  * Register handler. Enable vga_switcheroo if two vga clients have already
231a645654bSLukas Wunner  * registered.
232a645654bSLukas Wunner  *
233a645654bSLukas Wunner  * Return: 0 on success, -EINVAL if a handler was already registered.
234a645654bSLukas Wunner  */
vga_switcheroo_register_handler(const struct vga_switcheroo_handler * handler,enum vga_switcheroo_handler_flags_t handler_flags)235fa6d513aSJoan Jani int vga_switcheroo_register_handler(
236fa6d513aSJoan Jani 			  const struct vga_switcheroo_handler *handler,
237156d7d41SLukas Wunner 			  enum vga_switcheroo_handler_flags_t handler_flags)
23836704c0cSSeth Forshee {
23936704c0cSSeth Forshee 	mutex_lock(&vgasr_mutex);
24036704c0cSSeth Forshee 	if (vgasr_priv.handler) {
24136704c0cSSeth Forshee 		mutex_unlock(&vgasr_mutex);
24236704c0cSSeth Forshee 		return -EINVAL;
24336704c0cSSeth Forshee 	}
24436704c0cSSeth Forshee 
24536704c0cSSeth Forshee 	vgasr_priv.handler = handler;
246156d7d41SLukas Wunner 	vgasr_priv.handler_flags = handler_flags;
24736704c0cSSeth Forshee 	if (vga_switcheroo_ready()) {
2489b0be1ebSThierry Reding 		pr_info("enabled\n");
24936704c0cSSeth Forshee 		vga_switcheroo_enable();
25036704c0cSSeth Forshee 	}
25136704c0cSSeth Forshee 	mutex_unlock(&vgasr_mutex);
25236704c0cSSeth Forshee 	return 0;
25336704c0cSSeth Forshee }
25436704c0cSSeth Forshee EXPORT_SYMBOL(vga_switcheroo_register_handler);
25536704c0cSSeth Forshee 
256a645654bSLukas Wunner /**
257a645654bSLukas Wunner  * vga_switcheroo_unregister_handler() - unregister handler
258a645654bSLukas Wunner  *
259a645654bSLukas Wunner  * Unregister handler. Disable vga_switcheroo.
260a645654bSLukas Wunner  */
vga_switcheroo_unregister_handler(void)26136704c0cSSeth Forshee void vga_switcheroo_unregister_handler(void)
26236704c0cSSeth Forshee {
26336704c0cSSeth Forshee 	mutex_lock(&vgasr_mutex);
264e4cb81d7SLukas Wunner 	mutex_lock(&vgasr_priv.mux_hw_lock);
265156d7d41SLukas Wunner 	vgasr_priv.handler_flags = 0;
26636704c0cSSeth Forshee 	vgasr_priv.handler = NULL;
26736704c0cSSeth Forshee 	if (vgasr_priv.active) {
2689b0be1ebSThierry Reding 		pr_info("disabled\n");
26936704c0cSSeth Forshee 		vga_switcheroo_debugfs_fini(&vgasr_priv);
27036704c0cSSeth Forshee 		vgasr_priv.active = false;
27136704c0cSSeth Forshee 	}
272e4cb81d7SLukas Wunner 	mutex_unlock(&vgasr_priv.mux_hw_lock);
27336704c0cSSeth Forshee 	mutex_unlock(&vgasr_mutex);
27436704c0cSSeth Forshee }
27536704c0cSSeth Forshee EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
27636704c0cSSeth Forshee 
277156d7d41SLukas Wunner /**
278156d7d41SLukas Wunner  * vga_switcheroo_handler_flags() - obtain handler flags
279156d7d41SLukas Wunner  *
280156d7d41SLukas Wunner  * Helper for clients to obtain the handler flags bitmask.
281156d7d41SLukas Wunner  *
282156d7d41SLukas Wunner  * Return: Handler flags. A value of 0 means that no handler is registered
283156d7d41SLukas Wunner  * or that the handler has no special capabilities.
284156d7d41SLukas Wunner  */
vga_switcheroo_handler_flags(void)285156d7d41SLukas Wunner enum vga_switcheroo_handler_flags_t vga_switcheroo_handler_flags(void)
286156d7d41SLukas Wunner {
287156d7d41SLukas Wunner 	return vgasr_priv.handler_flags;
288156d7d41SLukas Wunner }
289156d7d41SLukas Wunner EXPORT_SYMBOL(vga_switcheroo_handler_flags);
290156d7d41SLukas Wunner 
register_client(struct pci_dev * pdev,const struct vga_switcheroo_client_ops * ops,enum vga_switcheroo_client_id id,struct pci_dev * vga_dev,bool active,bool driver_power_control)2913e9e63dbSTakashi Iwai static int register_client(struct pci_dev *pdev,
2923e9e63dbSTakashi Iwai 			   const struct vga_switcheroo_client_ops *ops,
2934aaf448fSJim Qu 			   enum vga_switcheroo_client_id id,
2944aaf448fSJim Qu 			   struct pci_dev *vga_dev,
2954aaf448fSJim Qu 			   bool active,
296fa3e967fSLukas Wunner 			   bool driver_power_control)
2976a9ee8afSDave Airlie {
29879721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
29979721e0aSTakashi Iwai 
30079721e0aSTakashi Iwai 	client = kzalloc(sizeof(*client), GFP_KERNEL);
30179721e0aSTakashi Iwai 	if (!client)
30279721e0aSTakashi Iwai 		return -ENOMEM;
30379721e0aSTakashi Iwai 
30479721e0aSTakashi Iwai 	client->pwr_state = VGA_SWITCHEROO_ON;
30579721e0aSTakashi Iwai 	client->pdev = pdev;
30626ec685fSTakashi Iwai 	client->ops = ops;
3073e9e63dbSTakashi Iwai 	client->id = id;
3083e9e63dbSTakashi Iwai 	client->active = active;
3090d69704aSDave Airlie 	client->driver_power_control = driver_power_control;
3104aaf448fSJim Qu 	client->vga_dev = vga_dev;
3116a9ee8afSDave Airlie 
3126a9ee8afSDave Airlie 	mutex_lock(&vgasr_mutex);
31379721e0aSTakashi Iwai 	list_add_tail(&client->list, &vgasr_priv.clients);
3143e9e63dbSTakashi Iwai 	if (client_is_vga(client))
31579721e0aSTakashi Iwai 		vgasr_priv.registered_clients++;
3166a9ee8afSDave Airlie 
31736704c0cSSeth Forshee 	if (vga_switcheroo_ready()) {
3189b0be1ebSThierry Reding 		pr_info("enabled\n");
3196a9ee8afSDave Airlie 		vga_switcheroo_enable();
3206a9ee8afSDave Airlie 	}
3216a9ee8afSDave Airlie 	mutex_unlock(&vgasr_mutex);
3226a9ee8afSDave Airlie 	return 0;
3236a9ee8afSDave Airlie }
3243e9e63dbSTakashi Iwai 
325a645654bSLukas Wunner /**
326a645654bSLukas Wunner  * vga_switcheroo_register_client - register vga client
327a645654bSLukas Wunner  * @pdev: client pci device
328a645654bSLukas Wunner  * @ops: client callbacks
329a645654bSLukas Wunner  * @driver_power_control: whether power state is controlled by the driver's
330a645654bSLukas Wunner  *	runtime pm
331a645654bSLukas Wunner  *
332a645654bSLukas Wunner  * Register vga client (GPU). Enable vga_switcheroo if another GPU and a
333a645654bSLukas Wunner  * handler have already registered. The power state of the client is assumed
334b00e5334SLukas Wunner  * to be ON. Beforehand, vga_switcheroo_client_probe_defer() shall be called
335b00e5334SLukas Wunner  * to ensure that all prerequisites are met.
336a645654bSLukas Wunner  *
337a645654bSLukas Wunner  * Return: 0 on success, -ENOMEM on memory allocation error.
338a645654bSLukas Wunner  */
vga_switcheroo_register_client(struct pci_dev * pdev,const struct vga_switcheroo_client_ops * ops,bool driver_power_control)3393e9e63dbSTakashi Iwai int vga_switcheroo_register_client(struct pci_dev *pdev,
3400d69704aSDave Airlie 				   const struct vga_switcheroo_client_ops *ops,
3410d69704aSDave Airlie 				   bool driver_power_control)
3423e9e63dbSTakashi Iwai {
3434aaf448fSJim Qu 	return register_client(pdev, ops, VGA_SWITCHEROO_UNKNOWN_ID, NULL,
3447491bfb4SThierry Reding 			       pdev == vga_default_device(),
3457491bfb4SThierry Reding 			       driver_power_control);
3463e9e63dbSTakashi Iwai }
3476a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_register_client);
3486a9ee8afSDave Airlie 
349a645654bSLukas Wunner /**
350a645654bSLukas Wunner  * vga_switcheroo_register_audio_client - register audio client
351a645654bSLukas Wunner  * @pdev: client pci device
352a645654bSLukas Wunner  * @ops: client callbacks
3534aaf448fSJim Qu  * @vga_dev:  pci device which is bound to current audio client
354a645654bSLukas Wunner  *
35507f4f97dSLukas Wunner  * Register audio client (audio device on a GPU). The client is assumed
35607f4f97dSLukas Wunner  * to use runtime PM. Beforehand, vga_switcheroo_client_probe_defer()
357a345918dSLukas Wunner  * shall be called to ensure that all prerequisites are met.
358a645654bSLukas Wunner  *
3594aaf448fSJim Qu  * Return: 0 on success, -ENOMEM on memory allocation error, -EINVAL on getting
3604aaf448fSJim Qu  * client id error.
361a645654bSLukas Wunner  */
vga_switcheroo_register_audio_client(struct pci_dev * pdev,const struct vga_switcheroo_client_ops * ops,struct pci_dev * vga_dev)3623e9e63dbSTakashi Iwai int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
3633e9e63dbSTakashi Iwai 			const struct vga_switcheroo_client_ops *ops,
3644aaf448fSJim Qu 			struct pci_dev *vga_dev)
3653e9e63dbSTakashi Iwai {
3664aaf448fSJim Qu 	enum vga_switcheroo_client_id id = VGA_SWITCHEROO_UNKNOWN_ID;
3674aaf448fSJim Qu 
3684aaf448fSJim Qu 	/*
3694aaf448fSJim Qu 	 * if vga_switcheroo has enabled, that mean two GPU clients and also
3704aaf448fSJim Qu 	 * handler are registered. Get audio client id from bound GPU client
3714aaf448fSJim Qu 	 * id directly, otherwise, set it as VGA_SWITCHEROO_UNKNOWN_ID,
3724aaf448fSJim Qu 	 * it will set to correct id in later when vga_switcheroo_enable()
3734aaf448fSJim Qu 	 * is called.
3744aaf448fSJim Qu 	 */
3754aaf448fSJim Qu 	mutex_lock(&vgasr_mutex);
3764aaf448fSJim Qu 	if (vgasr_priv.active) {
3774aaf448fSJim Qu 		id = vgasr_priv.handler->get_client_id(vga_dev);
3784aaf448fSJim Qu 		if (id < 0) {
3794aaf448fSJim Qu 			mutex_unlock(&vgasr_mutex);
3804aaf448fSJim Qu 			return -EINVAL;
3814aaf448fSJim Qu 		}
382fc09ab7aSTakashi Iwai 		/* notify if GPU has been already bound */
383fc09ab7aSTakashi Iwai 		if (ops->gpu_bound)
384fc09ab7aSTakashi Iwai 			ops->gpu_bound(pdev, id);
3854aaf448fSJim Qu 	}
3864aaf448fSJim Qu 	mutex_unlock(&vgasr_mutex);
3874aaf448fSJim Qu 
3884aaf448fSJim Qu 	return register_client(pdev, ops, id | ID_BIT_AUDIO, vga_dev,
3894aaf448fSJim Qu 			       false, true);
3903e9e63dbSTakashi Iwai }
3913e9e63dbSTakashi Iwai EXPORT_SYMBOL(vga_switcheroo_register_audio_client);
3923e9e63dbSTakashi Iwai 
39379721e0aSTakashi Iwai static struct vga_switcheroo_client *
find_client_from_pci(struct list_head * head,struct pci_dev * pdev)39479721e0aSTakashi Iwai find_client_from_pci(struct list_head *head, struct pci_dev *pdev)
39579721e0aSTakashi Iwai {
39679721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
3977491bfb4SThierry Reding 
39879721e0aSTakashi Iwai 	list_for_each_entry(client, head, list)
39979721e0aSTakashi Iwai 		if (client->pdev == pdev)
40079721e0aSTakashi Iwai 			return client;
40179721e0aSTakashi Iwai 	return NULL;
40279721e0aSTakashi Iwai }
40379721e0aSTakashi Iwai 
40479721e0aSTakashi Iwai static struct vga_switcheroo_client *
find_client_from_id(struct list_head * head,enum vga_switcheroo_client_id client_id)405fa3e967fSLukas Wunner find_client_from_id(struct list_head *head,
406fa3e967fSLukas Wunner 		    enum vga_switcheroo_client_id client_id)
40779721e0aSTakashi Iwai {
40879721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
4097491bfb4SThierry Reding 
41079721e0aSTakashi Iwai 	list_for_each_entry(client, head, list)
41179721e0aSTakashi Iwai 		if (client->id == client_id)
41279721e0aSTakashi Iwai 			return client;
41379721e0aSTakashi Iwai 	return NULL;
41479721e0aSTakashi Iwai }
41579721e0aSTakashi Iwai 
41679721e0aSTakashi Iwai static struct vga_switcheroo_client *
find_active_client(struct list_head * head)41779721e0aSTakashi Iwai find_active_client(struct list_head *head)
41879721e0aSTakashi Iwai {
41979721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
4207491bfb4SThierry Reding 
42179721e0aSTakashi Iwai 	list_for_each_entry(client, head, list)
42221b45676SLukas Wunner 		if (client->active)
42379721e0aSTakashi Iwai 			return client;
42479721e0aSTakashi Iwai 	return NULL;
42579721e0aSTakashi Iwai }
42679721e0aSTakashi Iwai 
427a645654bSLukas Wunner /**
428b00e5334SLukas Wunner  * vga_switcheroo_client_probe_defer() - whether to defer probing a given client
429b00e5334SLukas Wunner  * @pdev: client pci device
430b00e5334SLukas Wunner  *
431b00e5334SLukas Wunner  * Determine whether any prerequisites are not fulfilled to probe a given
432b00e5334SLukas Wunner  * client. Drivers shall invoke this early on in their ->probe callback
433b00e5334SLukas Wunner  * and return %-EPROBE_DEFER if it evaluates to %true. Thou shalt not
434b00e5334SLukas Wunner  * register the client ere thou hast called this.
435b00e5334SLukas Wunner  *
436b00e5334SLukas Wunner  * Return: %true if probing should be deferred, otherwise %false.
437b00e5334SLukas Wunner  */
vga_switcheroo_client_probe_defer(struct pci_dev * pdev)438b00e5334SLukas Wunner bool vga_switcheroo_client_probe_defer(struct pci_dev *pdev)
439b00e5334SLukas Wunner {
440a345918dSLukas Wunner 	if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
441b00e5334SLukas Wunner 		/*
442b00e5334SLukas Wunner 		 * apple-gmux is needed on pre-retina MacBook Pro
443b00e5334SLukas Wunner 		 * to probe the panel if pdev is the inactive GPU.
444b00e5334SLukas Wunner 		 */
445b00e5334SLukas Wunner 		if (apple_gmux_present() && pdev != vga_default_device() &&
446b00e5334SLukas Wunner 		    !vgasr_priv.handler_flags)
447b00e5334SLukas Wunner 			return true;
448a345918dSLukas Wunner 	}
449b00e5334SLukas Wunner 
450b00e5334SLukas Wunner 	return false;
451b00e5334SLukas Wunner }
452b00e5334SLukas Wunner EXPORT_SYMBOL(vga_switcheroo_client_probe_defer);
453b00e5334SLukas Wunner 
4548948ca1aSLukas Wunner static enum vga_switcheroo_state
vga_switcheroo_pwr_state(struct vga_switcheroo_client * client)4558948ca1aSLukas Wunner vga_switcheroo_pwr_state(struct vga_switcheroo_client *client)
4568948ca1aSLukas Wunner {
4578948ca1aSLukas Wunner 	if (client->driver_power_control)
4588948ca1aSLukas Wunner 		if (pm_runtime_enabled(&client->pdev->dev) &&
4598948ca1aSLukas Wunner 		    pm_runtime_active(&client->pdev->dev))
4608948ca1aSLukas Wunner 			return VGA_SWITCHEROO_ON;
4618948ca1aSLukas Wunner 		else
4628948ca1aSLukas Wunner 			return VGA_SWITCHEROO_OFF;
4638948ca1aSLukas Wunner 	else
4648948ca1aSLukas Wunner 		return client->pwr_state;
4658948ca1aSLukas Wunner }
4668948ca1aSLukas Wunner 
467b00e5334SLukas Wunner /**
468a645654bSLukas Wunner  * vga_switcheroo_get_client_state() - obtain power state of a given client
469a645654bSLukas Wunner  * @pdev: client pci device
470a645654bSLukas Wunner  *
471a645654bSLukas Wunner  * Obtain power state of a given client as seen from vga_switcheroo.
472a645654bSLukas Wunner  * The function is only called from hda_intel.c.
473a645654bSLukas Wunner  *
474a645654bSLukas Wunner  * Return: Power state.
475a645654bSLukas Wunner  */
vga_switcheroo_get_client_state(struct pci_dev * pdev)476203d027dSLukas Wunner enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *pdev)
477c8e9cf7bSTakashi Iwai {
478c8e9cf7bSTakashi Iwai 	struct vga_switcheroo_client *client;
4798f12a311SLukas Wunner 	enum vga_switcheroo_state ret;
480c8e9cf7bSTakashi Iwai 
4818f12a311SLukas Wunner 	mutex_lock(&vgasr_mutex);
482c8e9cf7bSTakashi Iwai 	client = find_client_from_pci(&vgasr_priv.clients, pdev);
483c8e9cf7bSTakashi Iwai 	if (!client)
4848f12a311SLukas Wunner 		ret = VGA_SWITCHEROO_NOT_FOUND;
4858f12a311SLukas Wunner 	else
4868948ca1aSLukas Wunner 		ret = vga_switcheroo_pwr_state(client);
4878f12a311SLukas Wunner 	mutex_unlock(&vgasr_mutex);
4888f12a311SLukas Wunner 	return ret;
489c8e9cf7bSTakashi Iwai }
490c8e9cf7bSTakashi Iwai EXPORT_SYMBOL(vga_switcheroo_get_client_state);
491c8e9cf7bSTakashi Iwai 
492a645654bSLukas Wunner /**
493a645654bSLukas Wunner  * vga_switcheroo_unregister_client() - unregister client
494a645654bSLukas Wunner  * @pdev: client pci device
495a645654bSLukas Wunner  *
496a645654bSLukas Wunner  * Unregister client. Disable vga_switcheroo if this is a vga client (GPU).
497a645654bSLukas Wunner  */
vga_switcheroo_unregister_client(struct pci_dev * pdev)4986a9ee8afSDave Airlie void vga_switcheroo_unregister_client(struct pci_dev *pdev)
4996a9ee8afSDave Airlie {
50079721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
5016a9ee8afSDave Airlie 
5026a9ee8afSDave Airlie 	mutex_lock(&vgasr_mutex);
50379721e0aSTakashi Iwai 	client = find_client_from_pci(&vgasr_priv.clients, pdev);
50479721e0aSTakashi Iwai 	if (client) {
5053e9e63dbSTakashi Iwai 		if (client_is_vga(client))
5063e9e63dbSTakashi Iwai 			vgasr_priv.registered_clients--;
50779721e0aSTakashi Iwai 		list_del(&client->list);
50879721e0aSTakashi Iwai 		kfree(client);
5096a9ee8afSDave Airlie 	}
5103e9e63dbSTakashi Iwai 	if (vgasr_priv.active && vgasr_priv.registered_clients < 2) {
5119b0be1ebSThierry Reding 		pr_info("disabled\n");
5126a9ee8afSDave Airlie 		vga_switcheroo_debugfs_fini(&vgasr_priv);
5136a9ee8afSDave Airlie 		vgasr_priv.active = false;
5143e9e63dbSTakashi Iwai 	}
5156a9ee8afSDave Airlie 	mutex_unlock(&vgasr_mutex);
5166a9ee8afSDave Airlie }
5176a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_unregister_client);
5186a9ee8afSDave Airlie 
519a645654bSLukas Wunner /**
520a645654bSLukas Wunner  * vga_switcheroo_client_fb_set() - set framebuffer of a given client
521a645654bSLukas Wunner  * @pdev: client pci device
522a645654bSLukas Wunner  * @info: framebuffer
523a645654bSLukas Wunner  *
524a645654bSLukas Wunner  * Set framebuffer of a given client. The console will be remapped to this
525a645654bSLukas Wunner  * on switching.
526a645654bSLukas Wunner  */
vga_switcheroo_client_fb_set(struct pci_dev * pdev,struct fb_info * info)5276a9ee8afSDave Airlie void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
5286a9ee8afSDave Airlie 				 struct fb_info *info)
5296a9ee8afSDave Airlie {
53079721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
5316a9ee8afSDave Airlie 
5326a9ee8afSDave Airlie 	mutex_lock(&vgasr_mutex);
53379721e0aSTakashi Iwai 	client = find_client_from_pci(&vgasr_priv.clients, pdev);
53479721e0aSTakashi Iwai 	if (client)
53579721e0aSTakashi Iwai 		client->fb_info = info;
5366a9ee8afSDave Airlie 	mutex_unlock(&vgasr_mutex);
5376a9ee8afSDave Airlie }
5386a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_client_fb_set);
5396a9ee8afSDave Airlie 
540a645654bSLukas Wunner /**
541e4cb81d7SLukas Wunner  * vga_switcheroo_lock_ddc() - temporarily switch DDC lines to a given client
542e4cb81d7SLukas Wunner  * @pdev: client pci device
543e4cb81d7SLukas Wunner  *
544e4cb81d7SLukas Wunner  * Temporarily switch DDC lines to the client identified by @pdev
545e4cb81d7SLukas Wunner  * (but leave the outputs otherwise switched to where they are).
546e4cb81d7SLukas Wunner  * This allows the inactive client to probe EDID. The DDC lines must
547e4cb81d7SLukas Wunner  * afterwards be switched back by calling vga_switcheroo_unlock_ddc(),
548e4cb81d7SLukas Wunner  * even if this function returns an error.
549e4cb81d7SLukas Wunner  *
550e4cb81d7SLukas Wunner  * Return: Previous DDC owner on success or a negative int on error.
551e4cb81d7SLukas Wunner  * Specifically, %-ENODEV if no handler has registered or if the handler
552e4cb81d7SLukas Wunner  * does not support switching the DDC lines. Also, a negative value
553e4cb81d7SLukas Wunner  * returned by the handler is propagated back to the caller.
554e4cb81d7SLukas Wunner  * The return value has merely an informational purpose for any caller
555e4cb81d7SLukas Wunner  * which might be interested in it. It is acceptable to ignore the return
556e4cb81d7SLukas Wunner  * value and simply rely on the result of the subsequent EDID probe,
557e4cb81d7SLukas Wunner  * which will be %NULL if DDC switching failed.
558e4cb81d7SLukas Wunner  */
vga_switcheroo_lock_ddc(struct pci_dev * pdev)559e4cb81d7SLukas Wunner int vga_switcheroo_lock_ddc(struct pci_dev *pdev)
560e4cb81d7SLukas Wunner {
561e4cb81d7SLukas Wunner 	enum vga_switcheroo_client_id id;
562e4cb81d7SLukas Wunner 
563e4cb81d7SLukas Wunner 	mutex_lock(&vgasr_priv.mux_hw_lock);
564e4cb81d7SLukas Wunner 	if (!vgasr_priv.handler || !vgasr_priv.handler->switch_ddc) {
565e4cb81d7SLukas Wunner 		vgasr_priv.old_ddc_owner = -ENODEV;
566e4cb81d7SLukas Wunner 		return -ENODEV;
567e4cb81d7SLukas Wunner 	}
568e4cb81d7SLukas Wunner 
569e4cb81d7SLukas Wunner 	id = vgasr_priv.handler->get_client_id(pdev);
570e4cb81d7SLukas Wunner 	vgasr_priv.old_ddc_owner = vgasr_priv.handler->switch_ddc(id);
571e4cb81d7SLukas Wunner 	return vgasr_priv.old_ddc_owner;
572e4cb81d7SLukas Wunner }
573e4cb81d7SLukas Wunner EXPORT_SYMBOL(vga_switcheroo_lock_ddc);
574e4cb81d7SLukas Wunner 
575e4cb81d7SLukas Wunner /**
576e4cb81d7SLukas Wunner  * vga_switcheroo_unlock_ddc() - switch DDC lines back to previous owner
577e4cb81d7SLukas Wunner  * @pdev: client pci device
578e4cb81d7SLukas Wunner  *
579e4cb81d7SLukas Wunner  * Switch DDC lines back to the previous owner after calling
580e4cb81d7SLukas Wunner  * vga_switcheroo_lock_ddc(). This must be called even if
581e4cb81d7SLukas Wunner  * vga_switcheroo_lock_ddc() returned an error.
582e4cb81d7SLukas Wunner  *
583e4cb81d7SLukas Wunner  * Return: Previous DDC owner on success (i.e. the client identifier of @pdev)
584e4cb81d7SLukas Wunner  * or a negative int on error.
585e4cb81d7SLukas Wunner  * Specifically, %-ENODEV if no handler has registered or if the handler
586e4cb81d7SLukas Wunner  * does not support switching the DDC lines. Also, a negative value
587e4cb81d7SLukas Wunner  * returned by the handler is propagated back to the caller.
588e4cb81d7SLukas Wunner  * Finally, invoking this function without calling vga_switcheroo_lock_ddc()
589e4cb81d7SLukas Wunner  * first is not allowed and will result in %-EINVAL.
590e4cb81d7SLukas Wunner  */
vga_switcheroo_unlock_ddc(struct pci_dev * pdev)591e4cb81d7SLukas Wunner int vga_switcheroo_unlock_ddc(struct pci_dev *pdev)
592e4cb81d7SLukas Wunner {
593e4cb81d7SLukas Wunner 	enum vga_switcheroo_client_id id;
594e4cb81d7SLukas Wunner 	int ret = vgasr_priv.old_ddc_owner;
595e4cb81d7SLukas Wunner 
596e4cb81d7SLukas Wunner 	if (WARN_ON_ONCE(!mutex_is_locked(&vgasr_priv.mux_hw_lock)))
597e4cb81d7SLukas Wunner 		return -EINVAL;
598e4cb81d7SLukas Wunner 
599e4cb81d7SLukas Wunner 	if (vgasr_priv.old_ddc_owner >= 0) {
600e4cb81d7SLukas Wunner 		id = vgasr_priv.handler->get_client_id(pdev);
601e4cb81d7SLukas Wunner 		if (vgasr_priv.old_ddc_owner != id)
602e4cb81d7SLukas Wunner 			ret = vgasr_priv.handler->switch_ddc(
603e4cb81d7SLukas Wunner 						     vgasr_priv.old_ddc_owner);
604e4cb81d7SLukas Wunner 	}
605e4cb81d7SLukas Wunner 	mutex_unlock(&vgasr_priv.mux_hw_lock);
606e4cb81d7SLukas Wunner 	return ret;
607e4cb81d7SLukas Wunner }
608e4cb81d7SLukas Wunner EXPORT_SYMBOL(vga_switcheroo_unlock_ddc);
609e4cb81d7SLukas Wunner 
610e4cb81d7SLukas Wunner /**
611a645654bSLukas Wunner  * DOC: Manual switching and manual power control
612a645654bSLukas Wunner  *
613a645654bSLukas Wunner  * In this mode of use, the file /sys/kernel/debug/vgaswitcheroo/switch
614a645654bSLukas Wunner  * can be read to retrieve the current vga_switcheroo state and commands
615a645654bSLukas Wunner  * can be written to it to change the state. The file appears as soon as
616a645654bSLukas Wunner  * two GPU drivers and one handler have registered with vga_switcheroo.
617a645654bSLukas Wunner  * The following commands are understood:
618a645654bSLukas Wunner  *
619a645654bSLukas Wunner  * * OFF: Power off the device not in use.
620a645654bSLukas Wunner  * * ON: Power on the device not in use.
621a645654bSLukas Wunner  * * IGD: Switch to the integrated graphics device.
622a645654bSLukas Wunner  *   Power on the integrated GPU if necessary, power off the discrete GPU.
623a645654bSLukas Wunner  *   Prerequisite is that no user space processes (e.g. Xorg, alsactl)
624a645654bSLukas Wunner  *   have opened device files of the GPUs or the audio client. If the
625a645654bSLukas Wunner  *   switch fails, the user may invoke lsof(8) or fuser(1) on /dev/dri/
626a645654bSLukas Wunner  *   and /dev/snd/controlC1 to identify processes blocking the switch.
627a645654bSLukas Wunner  * * DIS: Switch to the discrete graphics device.
628a645654bSLukas Wunner  * * DIGD: Delayed switch to the integrated graphics device.
629a645654bSLukas Wunner  *   This will perform the switch once the last user space process has
630a645654bSLukas Wunner  *   closed the device files of the GPUs and the audio client.
631a645654bSLukas Wunner  * * DDIS: Delayed switch to the discrete graphics device.
632a645654bSLukas Wunner  * * MIGD: Mux-only switch to the integrated graphics device.
633a645654bSLukas Wunner  *   Does not remap console or change the power state of either gpu.
634a645654bSLukas Wunner  *   If the integrated GPU is currently off, the screen will turn black.
635a645654bSLukas Wunner  *   If it is on, the screen will show whatever happens to be in VRAM.
636a645654bSLukas Wunner  *   Either way, the user has to blindly enter the command to switch back.
637a645654bSLukas Wunner  * * MDIS: Mux-only switch to the discrete graphics device.
638a645654bSLukas Wunner  *
639a645654bSLukas Wunner  * For GPUs whose power state is controlled by the driver's runtime pm,
640a645654bSLukas Wunner  * the ON and OFF commands are a no-op (see next section).
641a645654bSLukas Wunner  *
642a645654bSLukas Wunner  * For muxless machines, the IGD/DIS, DIGD/DDIS and MIGD/MDIS commands
643a645654bSLukas Wunner  * should not be used.
644a645654bSLukas Wunner  */
645a645654bSLukas Wunner 
vga_switcheroo_show(struct seq_file * m,void * v)6466a9ee8afSDave Airlie static int vga_switcheroo_show(struct seq_file *m, void *v)
6476a9ee8afSDave Airlie {
64879721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
64979721e0aSTakashi Iwai 	int i = 0;
6507491bfb4SThierry Reding 
6516a9ee8afSDave Airlie 	mutex_lock(&vgasr_mutex);
65279721e0aSTakashi Iwai 	list_for_each_entry(client, &vgasr_priv.clients, list) {
6530d69704aSDave Airlie 		seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i,
6547491bfb4SThierry Reding 			   client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" :
6557491bfb4SThierry Reding 								     "IGD",
6563e9e63dbSTakashi Iwai 			   client_is_vga(client) ? "" : "-Audio",
65779721e0aSTakashi Iwai 			   client->active ? '+' : ' ',
6580d69704aSDave Airlie 			   client->driver_power_control ? "Dyn" : "",
6598948ca1aSLukas Wunner 			   vga_switcheroo_pwr_state(client) ? "Pwr" : "Off",
66079721e0aSTakashi Iwai 			   pci_name(client->pdev));
66179721e0aSTakashi Iwai 		i++;
6626a9ee8afSDave Airlie 	}
6636a9ee8afSDave Airlie 	mutex_unlock(&vgasr_mutex);
6646a9ee8afSDave Airlie 	return 0;
6656a9ee8afSDave Airlie }
6666a9ee8afSDave Airlie 
vga_switcheroo_debugfs_open(struct inode * inode,struct file * file)6676a9ee8afSDave Airlie static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
6686a9ee8afSDave Airlie {
6696a9ee8afSDave Airlie 	return single_open(file, vga_switcheroo_show, NULL);
6706a9ee8afSDave Airlie }
6716a9ee8afSDave Airlie 
vga_switchon(struct vga_switcheroo_client * client)6726a9ee8afSDave Airlie static int vga_switchon(struct vga_switcheroo_client *client)
6736a9ee8afSDave Airlie {
6740d69704aSDave Airlie 	if (client->driver_power_control)
6750d69704aSDave Airlie 		return 0;
6765cfb3c3aSDave Airlie 	if (vgasr_priv.handler->power_state)
6775cfb3c3aSDave Airlie 		vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);
6786a9ee8afSDave Airlie 	/* call the driver callback to turn on device */
67926ec685fSTakashi Iwai 	client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON);
6806a9ee8afSDave Airlie 	client->pwr_state = VGA_SWITCHEROO_ON;
6816a9ee8afSDave Airlie 	return 0;
6826a9ee8afSDave Airlie }
6836a9ee8afSDave Airlie 
vga_switchoff(struct vga_switcheroo_client * client)6846a9ee8afSDave Airlie static int vga_switchoff(struct vga_switcheroo_client *client)
6856a9ee8afSDave Airlie {
6860d69704aSDave Airlie 	if (client->driver_power_control)
6870d69704aSDave Airlie 		return 0;
6886a9ee8afSDave Airlie 	/* call the driver callback to turn off device */
68926ec685fSTakashi Iwai 	client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);
6905cfb3c3aSDave Airlie 	if (vgasr_priv.handler->power_state)
6916a9ee8afSDave Airlie 		vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF);
6926a9ee8afSDave Airlie 	client->pwr_state = VGA_SWITCHEROO_OFF;
6936a9ee8afSDave Airlie 	return 0;
6946a9ee8afSDave Airlie }
6956a9ee8afSDave Airlie 
set_audio_state(enum vga_switcheroo_client_id id,enum vga_switcheroo_state state)696fa3e967fSLukas Wunner static void set_audio_state(enum vga_switcheroo_client_id id,
697fa3e967fSLukas Wunner 			    enum vga_switcheroo_state state)
6983e9e63dbSTakashi Iwai {
6993e9e63dbSTakashi Iwai 	struct vga_switcheroo_client *client;
7003e9e63dbSTakashi Iwai 
7013e9e63dbSTakashi Iwai 	client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO);
70207f4f97dSLukas Wunner 	if (client)
7033e9e63dbSTakashi Iwai 		client->ops->set_gpu_state(client->pdev, state);
7043e9e63dbSTakashi Iwai }
7053e9e63dbSTakashi Iwai 
70666b37c67SDave Airlie /* stage one happens before delay */
vga_switchto_stage1(struct vga_switcheroo_client * new_client)70766b37c67SDave Airlie static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
7086a9ee8afSDave Airlie {
70979721e0aSTakashi Iwai 	struct vga_switcheroo_client *active;
7106a9ee8afSDave Airlie 
71179721e0aSTakashi Iwai 	active = find_active_client(&vgasr_priv.clients);
7126a9ee8afSDave Airlie 	if (!active)
7136a9ee8afSDave Airlie 		return 0;
7146a9ee8afSDave Airlie 
7158948ca1aSLukas Wunner 	if (vga_switcheroo_pwr_state(new_client) == VGA_SWITCHEROO_OFF)
7166a9ee8afSDave Airlie 		vga_switchon(new_client);
7176a9ee8afSDave Airlie 
7182fbe8c7cSMatthew Garrett 	vga_set_default_device(new_client->pdev);
71966b37c67SDave Airlie 	return 0;
72066b37c67SDave Airlie }
72166b37c67SDave Airlie 
72266b37c67SDave Airlie /* post delay */
vga_switchto_stage2(struct vga_switcheroo_client * new_client)72366b37c67SDave Airlie static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
72466b37c67SDave Airlie {
72566b37c67SDave Airlie 	int ret;
72679721e0aSTakashi Iwai 	struct vga_switcheroo_client *active;
72766b37c67SDave Airlie 
72879721e0aSTakashi Iwai 	active = find_active_client(&vgasr_priv.clients);
72966b37c67SDave Airlie 	if (!active)
73066b37c67SDave Airlie 		return 0;
73166b37c67SDave Airlie 
73266b37c67SDave Airlie 	active->active = false;
7336a9ee8afSDave Airlie 
734b67ae78eSLukas Wunner 	/* let HDA controller autosuspend if GPU uses driver power control */
735b67ae78eSLukas Wunner 	if (!active->driver_power_control)
736c91c3faeSTakashi Iwai 		set_audio_state(active->id, VGA_SWITCHEROO_OFF);
737c91c3faeSTakashi Iwai 
7381cd51b5dSDaniel Vetter 	if (new_client->fb_info)
7391cd51b5dSDaniel Vetter 		fbcon_remap_all(new_client->fb_info);
7406a9ee8afSDave Airlie 
741e4cb81d7SLukas Wunner 	mutex_lock(&vgasr_priv.mux_hw_lock);
7426a9ee8afSDave Airlie 	ret = vgasr_priv.handler->switchto(new_client->id);
743e4cb81d7SLukas Wunner 	mutex_unlock(&vgasr_priv.mux_hw_lock);
7446a9ee8afSDave Airlie 	if (ret)
7456a9ee8afSDave Airlie 		return ret;
7466a9ee8afSDave Airlie 
74726ec685fSTakashi Iwai 	if (new_client->ops->reprobe)
74826ec685fSTakashi Iwai 		new_client->ops->reprobe(new_client->pdev);
7498d608aa6SDave Airlie 
7508948ca1aSLukas Wunner 	if (vga_switcheroo_pwr_state(active) == VGA_SWITCHEROO_ON)
7516a9ee8afSDave Airlie 		vga_switchoff(active);
7526a9ee8afSDave Airlie 
753b67ae78eSLukas Wunner 	/* let HDA controller autoresume if GPU uses driver power control */
754b67ae78eSLukas Wunner 	if (!new_client->driver_power_control)
755c91c3faeSTakashi Iwai 		set_audio_state(new_client->id, VGA_SWITCHEROO_ON);
756c91c3faeSTakashi Iwai 
7576a9ee8afSDave Airlie 	new_client->active = true;
7586a9ee8afSDave Airlie 	return 0;
7596a9ee8afSDave Airlie }
7606a9ee8afSDave Airlie 
check_can_switch(void)76179721e0aSTakashi Iwai static bool check_can_switch(void)
76279721e0aSTakashi Iwai {
76379721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
76479721e0aSTakashi Iwai 
76579721e0aSTakashi Iwai 	list_for_each_entry(client, &vgasr_priv.clients, list) {
76626ec685fSTakashi Iwai 		if (!client->ops->can_switch(client->pdev)) {
7679b0be1ebSThierry Reding 			pr_err("client %x refused switch\n", client->id);
76879721e0aSTakashi Iwai 			return false;
76979721e0aSTakashi Iwai 		}
77079721e0aSTakashi Iwai 	}
77179721e0aSTakashi Iwai 	return true;
77279721e0aSTakashi Iwai }
77379721e0aSTakashi Iwai 
7746a9ee8afSDave Airlie static ssize_t
vga_switcheroo_debugfs_write(struct file * filp,const char __user * ubuf,size_t cnt,loff_t * ppos)7756a9ee8afSDave Airlie vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
7766a9ee8afSDave Airlie 			     size_t cnt, loff_t *ppos)
7776a9ee8afSDave Airlie {
7786a9ee8afSDave Airlie 	char usercmd[64];
77979721e0aSTakashi Iwai 	int ret;
7806a9ee8afSDave Airlie 	bool delay = false, can_switch;
781851ab954SDave Airlie 	bool just_mux = false;
782fa3e967fSLukas Wunner 	enum vga_switcheroo_client_id client_id = VGA_SWITCHEROO_UNKNOWN_ID;
7836a9ee8afSDave Airlie 	struct vga_switcheroo_client *client = NULL;
7846a9ee8afSDave Airlie 
7856a9ee8afSDave Airlie 	if (cnt > 63)
7866a9ee8afSDave Airlie 		cnt = 63;
7876a9ee8afSDave Airlie 
7886a9ee8afSDave Airlie 	if (copy_from_user(usercmd, ubuf, cnt))
7896a9ee8afSDave Airlie 		return -EFAULT;
7906a9ee8afSDave Airlie 
7916a9ee8afSDave Airlie 	mutex_lock(&vgasr_mutex);
7926a9ee8afSDave Airlie 
7938c88e50bSJiri Slaby 	if (!vgasr_priv.active) {
7948c88e50bSJiri Slaby 		cnt = -EINVAL;
7958c88e50bSJiri Slaby 		goto out;
7968c88e50bSJiri Slaby 	}
7976a9ee8afSDave Airlie 
7986a9ee8afSDave Airlie 	/* pwr off the device not in use */
7996a9ee8afSDave Airlie 	if (strncmp(usercmd, "OFF", 3) == 0) {
80079721e0aSTakashi Iwai 		list_for_each_entry(client, &vgasr_priv.clients, list) {
801c91c3faeSTakashi Iwai 			if (client->active || client_is_audio(client))
8026a9ee8afSDave Airlie 				continue;
8030d69704aSDave Airlie 			if (client->driver_power_control)
8040d69704aSDave Airlie 				continue;
805c91c3faeSTakashi Iwai 			set_audio_state(client->id, VGA_SWITCHEROO_OFF);
80679721e0aSTakashi Iwai 			if (client->pwr_state == VGA_SWITCHEROO_ON)
80779721e0aSTakashi Iwai 				vga_switchoff(client);
8086a9ee8afSDave Airlie 		}
8096a9ee8afSDave Airlie 		goto out;
8106a9ee8afSDave Airlie 	}
8116a9ee8afSDave Airlie 	/* pwr on the device not in use */
8126a9ee8afSDave Airlie 	if (strncmp(usercmd, "ON", 2) == 0) {
81379721e0aSTakashi Iwai 		list_for_each_entry(client, &vgasr_priv.clients, list) {
814c91c3faeSTakashi Iwai 			if (client->active || client_is_audio(client))
8156a9ee8afSDave Airlie 				continue;
8160d69704aSDave Airlie 			if (client->driver_power_control)
8170d69704aSDave Airlie 				continue;
81879721e0aSTakashi Iwai 			if (client->pwr_state == VGA_SWITCHEROO_OFF)
81979721e0aSTakashi Iwai 				vga_switchon(client);
820c91c3faeSTakashi Iwai 			set_audio_state(client->id, VGA_SWITCHEROO_ON);
8216a9ee8afSDave Airlie 		}
8226a9ee8afSDave Airlie 		goto out;
8236a9ee8afSDave Airlie 	}
8246a9ee8afSDave Airlie 
8256a9ee8afSDave Airlie 	/* request a delayed switch - test can we switch now */
8266a9ee8afSDave Airlie 	if (strncmp(usercmd, "DIGD", 4) == 0) {
8276a9ee8afSDave Airlie 		client_id = VGA_SWITCHEROO_IGD;
8286a9ee8afSDave Airlie 		delay = true;
8296a9ee8afSDave Airlie 	}
8306a9ee8afSDave Airlie 
8316a9ee8afSDave Airlie 	if (strncmp(usercmd, "DDIS", 4) == 0) {
8326a9ee8afSDave Airlie 		client_id = VGA_SWITCHEROO_DIS;
8336a9ee8afSDave Airlie 		delay = true;
8346a9ee8afSDave Airlie 	}
8356a9ee8afSDave Airlie 
8366a9ee8afSDave Airlie 	if (strncmp(usercmd, "IGD", 3) == 0)
8376a9ee8afSDave Airlie 		client_id = VGA_SWITCHEROO_IGD;
8386a9ee8afSDave Airlie 
8396a9ee8afSDave Airlie 	if (strncmp(usercmd, "DIS", 3) == 0)
8406a9ee8afSDave Airlie 		client_id = VGA_SWITCHEROO_DIS;
8416a9ee8afSDave Airlie 
842fea6f330SDan Carpenter 	if (strncmp(usercmd, "MIGD", 4) == 0) {
843851ab954SDave Airlie 		just_mux = true;
844851ab954SDave Airlie 		client_id = VGA_SWITCHEROO_IGD;
845851ab954SDave Airlie 	}
846fea6f330SDan Carpenter 	if (strncmp(usercmd, "MDIS", 4) == 0) {
847851ab954SDave Airlie 		just_mux = true;
848851ab954SDave Airlie 		client_id = VGA_SWITCHEROO_DIS;
849851ab954SDave Airlie 	}
850851ab954SDave Airlie 
85121c5ba8cSLukas Wunner 	if (client_id == VGA_SWITCHEROO_UNKNOWN_ID)
8526a9ee8afSDave Airlie 		goto out;
85379721e0aSTakashi Iwai 	client = find_client_from_id(&vgasr_priv.clients, client_id);
85479721e0aSTakashi Iwai 	if (!client)
85579721e0aSTakashi Iwai 		goto out;
8566a9ee8afSDave Airlie 
8576a9ee8afSDave Airlie 	vgasr_priv.delayed_switch_active = false;
858851ab954SDave Airlie 
859851ab954SDave Airlie 	if (just_mux) {
860e4cb81d7SLukas Wunner 		mutex_lock(&vgasr_priv.mux_hw_lock);
861851ab954SDave Airlie 		ret = vgasr_priv.handler->switchto(client_id);
862e4cb81d7SLukas Wunner 		mutex_unlock(&vgasr_priv.mux_hw_lock);
863851ab954SDave Airlie 		goto out;
864851ab954SDave Airlie 	}
865851ab954SDave Airlie 
86679721e0aSTakashi Iwai 	if (client->active)
867a67b8887SFlorian Mickler 		goto out;
868a67b8887SFlorian Mickler 
8696a9ee8afSDave Airlie 	/* okay we want a switch - test if devices are willing to switch */
87079721e0aSTakashi Iwai 	can_switch = check_can_switch();
8716a9ee8afSDave Airlie 
8726a9ee8afSDave Airlie 	if (can_switch == false && delay == false)
8736a9ee8afSDave Airlie 		goto out;
8746a9ee8afSDave Airlie 
87579721e0aSTakashi Iwai 	if (can_switch) {
87666b37c67SDave Airlie 		ret = vga_switchto_stage1(client);
8776a9ee8afSDave Airlie 		if (ret)
8789b0be1ebSThierry Reding 			pr_err("switching failed stage 1 %d\n", ret);
87966b37c67SDave Airlie 
88066b37c67SDave Airlie 		ret = vga_switchto_stage2(client);
88166b37c67SDave Airlie 		if (ret)
8829b0be1ebSThierry Reding 			pr_err("switching failed stage 2 %d\n", ret);
88366b37c67SDave Airlie 
8846a9ee8afSDave Airlie 	} else {
8859b0be1ebSThierry Reding 		pr_info("setting delayed switch to client %d\n", client->id);
8866a9ee8afSDave Airlie 		vgasr_priv.delayed_switch_active = true;
8876a9ee8afSDave Airlie 		vgasr_priv.delayed_client_id = client_id;
8886a9ee8afSDave Airlie 
88966b37c67SDave Airlie 		ret = vga_switchto_stage1(client);
89066b37c67SDave Airlie 		if (ret)
8919b0be1ebSThierry Reding 			pr_err("delayed switching stage 1 failed %d\n", ret);
8926a9ee8afSDave Airlie 	}
8936a9ee8afSDave Airlie 
8946a9ee8afSDave Airlie out:
8956a9ee8afSDave Airlie 	mutex_unlock(&vgasr_mutex);
8966a9ee8afSDave Airlie 	return cnt;
8976a9ee8afSDave Airlie }
8986a9ee8afSDave Airlie 
8996a9ee8afSDave Airlie static const struct file_operations vga_switcheroo_debugfs_fops = {
9006a9ee8afSDave Airlie 	.owner = THIS_MODULE,
9016a9ee8afSDave Airlie 	.open = vga_switcheroo_debugfs_open,
9026a9ee8afSDave Airlie 	.write = vga_switcheroo_debugfs_write,
9036a9ee8afSDave Airlie 	.read = seq_read,
9046a9ee8afSDave Airlie 	.llseek = seq_lseek,
9056a9ee8afSDave Airlie 	.release = single_release,
9066a9ee8afSDave Airlie };
9076a9ee8afSDave Airlie 
vga_switcheroo_debugfs_fini(struct vgasr_priv * priv)9086a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv)
9096a9ee8afSDave Airlie {
91090e73491SGreg Kroah-Hartman 	debugfs_remove_recursive(priv->debugfs_root);
9116a9ee8afSDave Airlie 	priv->debugfs_root = NULL;
9126a9ee8afSDave Airlie }
9136a9ee8afSDave Airlie 
vga_switcheroo_debugfs_init(struct vgasr_priv * priv)91490e73491SGreg Kroah-Hartman static void vga_switcheroo_debugfs_init(struct vgasr_priv *priv)
9156a9ee8afSDave Airlie {
9166a9ee8afSDave Airlie 	/* already initialised */
9176a9ee8afSDave Airlie 	if (priv->debugfs_root)
91890e73491SGreg Kroah-Hartman 		return;
91990e73491SGreg Kroah-Hartman 
9206a9ee8afSDave Airlie 	priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL);
9216a9ee8afSDave Airlie 
92290e73491SGreg Kroah-Hartman 	debugfs_create_file("switch", 0644, priv->debugfs_root, NULL,
9237491bfb4SThierry Reding 			    &vga_switcheroo_debugfs_fops);
9246a9ee8afSDave Airlie }
9256a9ee8afSDave Airlie 
926a645654bSLukas Wunner /**
927a645654bSLukas Wunner  * vga_switcheroo_process_delayed_switch() - helper for delayed switching
928a645654bSLukas Wunner  *
929a645654bSLukas Wunner  * Process a delayed switch if one is pending. DRM drivers should call this
930a645654bSLukas Wunner  * from their ->lastclose callback.
931a645654bSLukas Wunner  *
932a645654bSLukas Wunner  * Return: 0 on success. -EINVAL if no delayed switch is pending, if the client
933a645654bSLukas Wunner  * has unregistered in the meantime or if there are other clients blocking the
934a645654bSLukas Wunner  * switch. If the actual switch fails, an error is reported and 0 is returned.
935a645654bSLukas Wunner  */
vga_switcheroo_process_delayed_switch(void)9366a9ee8afSDave Airlie int vga_switcheroo_process_delayed_switch(void)
9376a9ee8afSDave Airlie {
93879721e0aSTakashi Iwai 	struct vga_switcheroo_client *client;
9396a9ee8afSDave Airlie 	int ret;
9406a9ee8afSDave Airlie 	int err = -EINVAL;
9416a9ee8afSDave Airlie 
9426a9ee8afSDave Airlie 	mutex_lock(&vgasr_mutex);
9436a9ee8afSDave Airlie 	if (!vgasr_priv.delayed_switch_active)
9446a9ee8afSDave Airlie 		goto err;
9456a9ee8afSDave Airlie 
9469b0be1ebSThierry Reding 	pr_info("processing delayed switch to %d\n",
9479b0be1ebSThierry Reding 		vgasr_priv.delayed_client_id);
9486a9ee8afSDave Airlie 
94979721e0aSTakashi Iwai 	client = find_client_from_id(&vgasr_priv.clients,
95079721e0aSTakashi Iwai 				     vgasr_priv.delayed_client_id);
95179721e0aSTakashi Iwai 	if (!client || !check_can_switch())
9526a9ee8afSDave Airlie 		goto err;
9536a9ee8afSDave Airlie 
95466b37c67SDave Airlie 	ret = vga_switchto_stage2(client);
9556a9ee8afSDave Airlie 	if (ret)
9569b0be1ebSThierry Reding 		pr_err("delayed switching failed stage 2 %d\n", ret);
9576a9ee8afSDave Airlie 
9586a9ee8afSDave Airlie 	vgasr_priv.delayed_switch_active = false;
9596a9ee8afSDave Airlie 	err = 0;
9606a9ee8afSDave Airlie err:
9616a9ee8afSDave Airlie 	mutex_unlock(&vgasr_mutex);
9626a9ee8afSDave Airlie 	return err;
9636a9ee8afSDave Airlie }
9646a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
9650d69704aSDave Airlie 
966a645654bSLukas Wunner /**
967a645654bSLukas Wunner  * DOC: Driver power control
968a645654bSLukas Wunner  *
969a645654bSLukas Wunner  * In this mode of use, the discrete GPU automatically powers up and down at
970a645654bSLukas Wunner  * the discretion of the driver's runtime pm. On muxed machines, the user may
971a645654bSLukas Wunner  * still influence the muxer state by way of the debugfs interface, however
972a645654bSLukas Wunner  * the ON and OFF commands become a no-op for the discrete GPU.
973a645654bSLukas Wunner  *
974a645654bSLukas Wunner  * This mode is the default on Nvidia HybridPower/Optimus and ATI PowerXpress.
975a645654bSLukas Wunner  * Specifying nouveau.runpm=0, radeon.runpm=0 or amdgpu.runpm=0 on the kernel
976a645654bSLukas Wunner  * command line disables it.
977a645654bSLukas Wunner  *
978a645654bSLukas Wunner  * After the GPU has been suspended, the handler needs to be called to cut
979a645654bSLukas Wunner  * power to the GPU. Likewise it needs to reinstate power before the GPU
980a645654bSLukas Wunner  * can resume. This is achieved by vga_switcheroo_init_domain_pm_ops(),
981a645654bSLukas Wunner  * which augments the GPU's suspend/resume functions by the requisite
982a645654bSLukas Wunner  * calls to the handler.
983a645654bSLukas Wunner  *
984a645654bSLukas Wunner  * When the audio device resumes, the GPU needs to be woken. This is achieved
98507f4f97dSLukas Wunner  * by a PCI quirk which calls device_link_add() to declare a dependency on the
98607f4f97dSLukas Wunner  * GPU. That way, the GPU is kept awake whenever and as long as the audio
98707f4f97dSLukas Wunner  * device is in use.
988a645654bSLukas Wunner  *
989a645654bSLukas Wunner  * On muxed machines, if the mux is initially switched to the discrete GPU,
990a645654bSLukas Wunner  * the user ends up with a black screen when the GPU powers down after boot.
991a645654bSLukas Wunner  * As a workaround, the mux is forced to the integrated GPU on runtime suspend,
992a645654bSLukas Wunner  * cf. https://bugs.freedesktop.org/show_bug.cgi?id=75917
993a645654bSLukas Wunner  */
994a645654bSLukas Wunner 
vga_switcheroo_power_switch(struct pci_dev * pdev,enum vga_switcheroo_state state)9957491bfb4SThierry Reding static void vga_switcheroo_power_switch(struct pci_dev *pdev,
9967491bfb4SThierry Reding 					enum vga_switcheroo_state state)
9970d69704aSDave Airlie {
9980d69704aSDave Airlie 	struct vga_switcheroo_client *client;
9990d69704aSDave Airlie 
10000d69704aSDave Airlie 	if (!vgasr_priv.handler->power_state)
10010d69704aSDave Airlie 		return;
10020d69704aSDave Airlie 
10030d69704aSDave Airlie 	client = find_client_from_pci(&vgasr_priv.clients, pdev);
10040d69704aSDave Airlie 	if (!client)
10050d69704aSDave Airlie 		return;
10060d69704aSDave Airlie 
10070d69704aSDave Airlie 	if (!client->driver_power_control)
10080d69704aSDave Airlie 		return;
10090d69704aSDave Airlie 
10100d69704aSDave Airlie 	vgasr_priv.handler->power_state(client->id, state);
10110d69704aSDave Airlie }
10120d69704aSDave Airlie 
10130d69704aSDave Airlie /* switcheroo power domain */
vga_switcheroo_runtime_suspend(struct device * dev)10140d69704aSDave Airlie static int vga_switcheroo_runtime_suspend(struct device *dev)
10150d69704aSDave Airlie {
10160d69704aSDave Airlie 	struct pci_dev *pdev = to_pci_dev(dev);
10170d69704aSDave Airlie 	int ret;
10180d69704aSDave Airlie 
10190d69704aSDave Airlie 	ret = dev->bus->pm->runtime_suspend(dev);
10200d69704aSDave Airlie 	if (ret)
10210d69704aSDave Airlie 		return ret;
10228f12a311SLukas Wunner 	mutex_lock(&vgasr_mutex);
1023e4cb81d7SLukas Wunner 	if (vgasr_priv.handler->switchto) {
1024e4cb81d7SLukas Wunner 		mutex_lock(&vgasr_priv.mux_hw_lock);
1025f2bc5616SAlex Deucher 		vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD);
1026e4cb81d7SLukas Wunner 		mutex_unlock(&vgasr_priv.mux_hw_lock);
1027e4cb81d7SLukas Wunner 	}
1028dcac86b7SLukas Wunner 	pci_bus_set_current_state(pdev->bus, PCI_D3cold);
10290d69704aSDave Airlie 	vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF);
10308f12a311SLukas Wunner 	mutex_unlock(&vgasr_mutex);
10310d69704aSDave Airlie 	return 0;
10320d69704aSDave Airlie }
10330d69704aSDave Airlie 
vga_switcheroo_runtime_resume(struct device * dev)10340d69704aSDave Airlie static int vga_switcheroo_runtime_resume(struct device *dev)
10350d69704aSDave Airlie {
10360d69704aSDave Airlie 	struct pci_dev *pdev = to_pci_dev(dev);
10370d69704aSDave Airlie 
10380d69704aSDave Airlie 	mutex_lock(&vgasr_mutex);
10398f12a311SLukas Wunner 	vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_ON);
10400d69704aSDave Airlie 	mutex_unlock(&vgasr_mutex);
10418f12a311SLukas Wunner 	pci_resume_bus(pdev->bus);
1042*99efde6cSMika Westerberg 	return dev->bus->pm->runtime_resume(dev);
10430d69704aSDave Airlie }
10440d69704aSDave Airlie 
10450d69704aSDave Airlie /**
10460d69704aSDave Airlie  * vga_switcheroo_init_domain_pm_ops() - helper for driver power control
10470d69704aSDave Airlie  * @dev: vga client device
10480d69704aSDave Airlie  * @domain: power domain
10490d69704aSDave Airlie  *
1050a645654bSLukas Wunner  * Helper for GPUs whose power state is controlled by the driver's runtime pm.
1051a645654bSLukas Wunner  * After the GPU has been suspended, the handler needs to be called to cut
1052a645654bSLukas Wunner  * power to the GPU. Likewise it needs to reinstate power before the GPU
1053a645654bSLukas Wunner  * can resume. To this end, this helper augments the suspend/resume functions
1054a645654bSLukas Wunner  * by the requisite calls to the handler. It needs only be called on platforms
1055a645654bSLukas Wunner  * where the power switch is separate to the device being powered down.
1056a645654bSLukas Wunner  */
vga_switcheroo_init_domain_pm_ops(struct device * dev,struct dev_pm_domain * domain)1057a645654bSLukas Wunner int vga_switcheroo_init_domain_pm_ops(struct device *dev,
1058a645654bSLukas Wunner 				      struct dev_pm_domain *domain)
1059a645654bSLukas Wunner {
1060a645654bSLukas Wunner 	/* copy over all the bus versions */
1061a645654bSLukas Wunner 	if (dev->bus && dev->bus->pm) {
10627491bfb4SThierry Reding 		domain->ops = *dev->bus->pm;
10637491bfb4SThierry Reding 		domain->ops.runtime_suspend = vga_switcheroo_runtime_suspend;
10640d69704aSDave Airlie 		domain->ops.runtime_resume = vga_switcheroo_runtime_resume;
10650d69704aSDave Airlie 
10660d69704aSDave Airlie 		dev_pm_domain_set(dev, domain);
10670d69704aSDave Airlie 		return 0;
10680d69704aSDave Airlie 	}
10690d69704aSDave Airlie 	dev_pm_domain_set(dev, NULL);
10700d69704aSDave Airlie 	return -EINVAL;
1071989561deSTomeu Vizoso }
10720d69704aSDave Airlie EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops);
10730d69704aSDave Airlie 
vga_switcheroo_fini_domain_pm_ops(struct device * dev)1074989561deSTomeu Vizoso void vga_switcheroo_fini_domain_pm_ops(struct device *dev)
10750d69704aSDave Airlie {
10760d69704aSDave Airlie 	dev_pm_domain_set(dev, NULL);
10770d69704aSDave Airlie }
10780d69704aSDave Airlie EXPORT_SYMBOL(vga_switcheroo_fini_domain_pm_ops);
1079766a53d0SAlex Deucher