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 334127838cSLukas Wunner #include <linux/console.h> 346a9ee8afSDave Airlie #include <linux/debugfs.h> 356a9ee8afSDave Airlie #include <linux/fb.h> 364127838cSLukas Wunner #include <linux/fs.h> 374127838cSLukas Wunner #include <linux/module.h> 386a9ee8afSDave Airlie #include <linux/pci.h> 390d69704aSDave Airlie #include <linux/pm_runtime.h> 404127838cSLukas Wunner #include <linux/seq_file.h> 414127838cSLukas Wunner #include <linux/uaccess.h> 422fbe8c7cSMatthew Garrett #include <linux/vgaarb.h> 434127838cSLukas Wunner #include <linux/vga_switcheroo.h> 442fbe8c7cSMatthew Garrett 45a645654bSLukas Wunner /** 46a645654bSLukas Wunner * DOC: Overview 47a645654bSLukas Wunner * 48a645654bSLukas Wunner * vga_switcheroo is the Linux subsystem for laptop hybrid graphics. 49a645654bSLukas Wunner * These come in two flavors: 50a645654bSLukas Wunner * 51a645654bSLukas Wunner * * muxed: Dual GPUs with a multiplexer chip to switch outputs between GPUs. 52a645654bSLukas Wunner * * muxless: Dual GPUs but only one of them is connected to outputs. 53a645654bSLukas Wunner * The other one is merely used to offload rendering, its results 54a645654bSLukas Wunner * are copied over PCIe into the framebuffer. On Linux this is 55a645654bSLukas Wunner * supported with DRI PRIME. 56a645654bSLukas Wunner * 57a645654bSLukas Wunner * Hybrid graphics started to appear in the late Naughties and were initially 58a645654bSLukas Wunner * all muxed. Newer laptops moved to a muxless architecture for cost reasons. 59a645654bSLukas Wunner * A notable exception is the MacBook Pro which continues to use a mux. 60a645654bSLukas Wunner * Muxes come with varying capabilities: Some switch only the panel, others 61a645654bSLukas Wunner * can also switch external displays. Some switch all display pins at once 62a645654bSLukas Wunner * while others can switch just the DDC lines. (To allow EDID probing 63a645654bSLukas Wunner * for the inactive GPU.) Also, muxes are often used to cut power to the 64a645654bSLukas Wunner * discrete GPU while it is not used. 65a645654bSLukas Wunner * 66a645654bSLukas Wunner * DRM drivers register GPUs with vga_switcheroo, these are heretoforth called 67a645654bSLukas Wunner * clients. The mux is called the handler. Muxless machines also register a 68a645654bSLukas Wunner * handler to control the power state of the discrete GPU, its ->switchto 69a645654bSLukas Wunner * callback is a no-op for obvious reasons. The discrete GPU is often equipped 70a645654bSLukas Wunner * with an HDA controller for the HDMI/DP audio signal, this will also 71a645654bSLukas Wunner * register as a client so that vga_switcheroo can take care of the correct 72a645654bSLukas Wunner * suspend/resume order when changing the discrete GPU's power state. In total 73a645654bSLukas Wunner * there can thus be up to three clients: Two vga clients (GPUs) and one audio 74a645654bSLukas Wunner * client (on the discrete GPU). The code is mostly prepared to support 75a645654bSLukas Wunner * machines with more than two GPUs should they become available. 76a645654bSLukas Wunner * The GPU to which the outputs are currently switched is called the 77a645654bSLukas Wunner * active client in vga_switcheroo parlance. The GPU not in use is the 78a645654bSLukas Wunner * inactive client. 79a645654bSLukas Wunner */ 80a645654bSLukas Wunner 81a645654bSLukas Wunner /** 82a645654bSLukas Wunner * struct vga_switcheroo_client - registered client 83a645654bSLukas Wunner * @pdev: client pci device 84a645654bSLukas Wunner * @fb_info: framebuffer to which console is remapped on switching 85a645654bSLukas Wunner * @pwr_state: current power state 86a645654bSLukas Wunner * @ops: client callbacks 8721c5ba8cSLukas Wunner * @id: client identifier. Determining the id requires the handler, 8821c5ba8cSLukas Wunner * so gpus are initially assigned VGA_SWITCHEROO_UNKNOWN_ID 8921c5ba8cSLukas Wunner * and later given their true id in vga_switcheroo_enable() 90a645654bSLukas Wunner * @active: whether the outputs are currently switched to this client 91a645654bSLukas Wunner * @driver_power_control: whether power state is controlled by the driver's 92a645654bSLukas Wunner * runtime pm. If true, writing ON and OFF to the vga_switcheroo debugfs 93a645654bSLukas Wunner * interface is a no-op so as not to interfere with runtime pm 94a645654bSLukas Wunner * @list: client list 95a645654bSLukas Wunner * 96a645654bSLukas Wunner * Registered client. A client can be either a GPU or an audio device on a GPU. 97a645654bSLukas Wunner * For audio clients, the @fb_info, @active and @driver_power_control members 98a645654bSLukas Wunner * are bogus. 99a645654bSLukas Wunner */ 1006a9ee8afSDave Airlie struct vga_switcheroo_client { 1016a9ee8afSDave Airlie struct pci_dev *pdev; 1026a9ee8afSDave Airlie struct fb_info *fb_info; 103203d027dSLukas Wunner enum vga_switcheroo_state pwr_state; 10426ec685fSTakashi Iwai const struct vga_switcheroo_client_ops *ops; 105fa3e967fSLukas Wunner enum vga_switcheroo_client_id id; 1066a9ee8afSDave Airlie bool active; 1070d69704aSDave Airlie bool driver_power_control; 10879721e0aSTakashi Iwai struct list_head list; 1096a9ee8afSDave Airlie }; 1106a9ee8afSDave Airlie 111a645654bSLukas Wunner /* 112a645654bSLukas Wunner * protects access to struct vgasr_priv 113a645654bSLukas Wunner */ 1146a9ee8afSDave Airlie static DEFINE_MUTEX(vgasr_mutex); 1156a9ee8afSDave Airlie 116a645654bSLukas Wunner /** 117a645654bSLukas Wunner * struct vgasr_priv - vga_switcheroo private data 118a645654bSLukas Wunner * @active: whether vga_switcheroo is enabled. 119a645654bSLukas Wunner * Prerequisite is the registration of two GPUs and a handler 120a645654bSLukas Wunner * @delayed_switch_active: whether a delayed switch is pending 121a645654bSLukas Wunner * @delayed_client_id: client to which a delayed switch is pending 122a645654bSLukas Wunner * @debugfs_root: directory for vga_switcheroo debugfs interface 123a645654bSLukas Wunner * @switch_file: file for vga_switcheroo debugfs interface 124a645654bSLukas Wunner * @registered_clients: number of registered GPUs 125a645654bSLukas Wunner * (counting only vga clients, not audio clients) 126a645654bSLukas Wunner * @clients: list of registered clients 127a645654bSLukas Wunner * @handler: registered handler 128a645654bSLukas Wunner * 129a645654bSLukas Wunner * vga_switcheroo private data. Currently only one vga_switcheroo instance 130a645654bSLukas Wunner * per system is supported. 131a645654bSLukas Wunner */ 1326a9ee8afSDave Airlie struct vgasr_priv { 1336a9ee8afSDave Airlie bool active; 1346a9ee8afSDave Airlie bool delayed_switch_active; 1356a9ee8afSDave Airlie enum vga_switcheroo_client_id delayed_client_id; 1366a9ee8afSDave Airlie 1376a9ee8afSDave Airlie struct dentry *debugfs_root; 1386a9ee8afSDave Airlie struct dentry *switch_file; 1396a9ee8afSDave Airlie 1406a9ee8afSDave Airlie int registered_clients; 14179721e0aSTakashi Iwai struct list_head clients; 1426a9ee8afSDave Airlie 1436a9ee8afSDave Airlie struct vga_switcheroo_handler *handler; 1446a9ee8afSDave Airlie }; 1456a9ee8afSDave Airlie 1463e9e63dbSTakashi Iwai #define ID_BIT_AUDIO 0x100 1473e9e63dbSTakashi Iwai #define client_is_audio(c) ((c)->id & ID_BIT_AUDIO) 14821c5ba8cSLukas Wunner #define client_is_vga(c) ((c)->id == VGA_SWITCHEROO_UNKNOWN_ID || \ 14921c5ba8cSLukas Wunner !client_is_audio(c)) 1503e9e63dbSTakashi Iwai #define client_id(c) ((c)->id & ~ID_BIT_AUDIO) 1513e9e63dbSTakashi Iwai 1526a9ee8afSDave Airlie static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); 1536a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); 1546a9ee8afSDave Airlie 1556a9ee8afSDave Airlie /* only one switcheroo per system */ 15679721e0aSTakashi Iwai static struct vgasr_priv vgasr_priv = { 15779721e0aSTakashi Iwai .clients = LIST_HEAD_INIT(vgasr_priv.clients), 15879721e0aSTakashi Iwai }; 1596a9ee8afSDave Airlie 16036704c0cSSeth Forshee static bool vga_switcheroo_ready(void) 1616a9ee8afSDave Airlie { 16236704c0cSSeth Forshee /* we're ready if we get two clients + handler */ 16336704c0cSSeth Forshee return !vgasr_priv.active && 16436704c0cSSeth Forshee vgasr_priv.registered_clients == 2 && vgasr_priv.handler; 1656a9ee8afSDave Airlie } 1666a9ee8afSDave Airlie 1676a9ee8afSDave Airlie static void vga_switcheroo_enable(void) 1686a9ee8afSDave Airlie { 1696a9ee8afSDave Airlie int ret; 17079721e0aSTakashi Iwai struct vga_switcheroo_client *client; 17179721e0aSTakashi Iwai 1726a9ee8afSDave Airlie /* call the handler to init */ 173e99eac5eSSeth Forshee if (vgasr_priv.handler->init) 1746a9ee8afSDave Airlie vgasr_priv.handler->init(); 1756a9ee8afSDave Airlie 17679721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 17721c5ba8cSLukas Wunner if (client->id != VGA_SWITCHEROO_UNKNOWN_ID) 1783e9e63dbSTakashi Iwai continue; 17979721e0aSTakashi Iwai ret = vgasr_priv.handler->get_client_id(client->pdev); 1806a9ee8afSDave Airlie if (ret < 0) 1816a9ee8afSDave Airlie return; 1826a9ee8afSDave Airlie 18379721e0aSTakashi Iwai client->id = ret; 1846a9ee8afSDave Airlie } 1856a9ee8afSDave Airlie vga_switcheroo_debugfs_init(&vgasr_priv); 1866a9ee8afSDave Airlie vgasr_priv.active = true; 1876a9ee8afSDave Airlie } 1886a9ee8afSDave Airlie 189a645654bSLukas Wunner /** 190a645654bSLukas Wunner * vga_switcheroo_register_handler() - register handler 191a645654bSLukas Wunner * @handler: handler callbacks 192a645654bSLukas Wunner * 193a645654bSLukas Wunner * Register handler. Enable vga_switcheroo if two vga clients have already 194a645654bSLukas Wunner * registered. 195a645654bSLukas Wunner * 196a645654bSLukas Wunner * Return: 0 on success, -EINVAL if a handler was already registered. 197a645654bSLukas Wunner */ 19836704c0cSSeth Forshee int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) 19936704c0cSSeth Forshee { 20036704c0cSSeth Forshee mutex_lock(&vgasr_mutex); 20136704c0cSSeth Forshee if (vgasr_priv.handler) { 20236704c0cSSeth Forshee mutex_unlock(&vgasr_mutex); 20336704c0cSSeth Forshee return -EINVAL; 20436704c0cSSeth Forshee } 20536704c0cSSeth Forshee 20636704c0cSSeth Forshee vgasr_priv.handler = handler; 20736704c0cSSeth Forshee if (vga_switcheroo_ready()) { 2089b0be1ebSThierry Reding pr_info("enabled\n"); 20936704c0cSSeth Forshee vga_switcheroo_enable(); 21036704c0cSSeth Forshee } 21136704c0cSSeth Forshee mutex_unlock(&vgasr_mutex); 21236704c0cSSeth Forshee return 0; 21336704c0cSSeth Forshee } 21436704c0cSSeth Forshee EXPORT_SYMBOL(vga_switcheroo_register_handler); 21536704c0cSSeth Forshee 216a645654bSLukas Wunner /** 217a645654bSLukas Wunner * vga_switcheroo_unregister_handler() - unregister handler 218a645654bSLukas Wunner * 219a645654bSLukas Wunner * Unregister handler. Disable vga_switcheroo. 220a645654bSLukas Wunner */ 22136704c0cSSeth Forshee void vga_switcheroo_unregister_handler(void) 22236704c0cSSeth Forshee { 22336704c0cSSeth Forshee mutex_lock(&vgasr_mutex); 22436704c0cSSeth Forshee vgasr_priv.handler = NULL; 22536704c0cSSeth Forshee if (vgasr_priv.active) { 2269b0be1ebSThierry Reding pr_info("disabled\n"); 22736704c0cSSeth Forshee vga_switcheroo_debugfs_fini(&vgasr_priv); 22836704c0cSSeth Forshee vgasr_priv.active = false; 22936704c0cSSeth Forshee } 23036704c0cSSeth Forshee mutex_unlock(&vgasr_mutex); 23136704c0cSSeth Forshee } 23236704c0cSSeth Forshee EXPORT_SYMBOL(vga_switcheroo_unregister_handler); 23336704c0cSSeth Forshee 2343e9e63dbSTakashi Iwai static int register_client(struct pci_dev *pdev, 2353e9e63dbSTakashi Iwai const struct vga_switcheroo_client_ops *ops, 236fa3e967fSLukas Wunner enum vga_switcheroo_client_id id, bool active, 237fa3e967fSLukas Wunner bool driver_power_control) 2386a9ee8afSDave Airlie { 23979721e0aSTakashi Iwai struct vga_switcheroo_client *client; 24079721e0aSTakashi Iwai 24179721e0aSTakashi Iwai client = kzalloc(sizeof(*client), GFP_KERNEL); 24279721e0aSTakashi Iwai if (!client) 24379721e0aSTakashi Iwai return -ENOMEM; 24479721e0aSTakashi Iwai 24579721e0aSTakashi Iwai client->pwr_state = VGA_SWITCHEROO_ON; 24679721e0aSTakashi Iwai client->pdev = pdev; 24726ec685fSTakashi Iwai client->ops = ops; 2483e9e63dbSTakashi Iwai client->id = id; 2493e9e63dbSTakashi Iwai client->active = active; 2500d69704aSDave Airlie client->driver_power_control = driver_power_control; 2516a9ee8afSDave Airlie 2526a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 25379721e0aSTakashi Iwai list_add_tail(&client->list, &vgasr_priv.clients); 2543e9e63dbSTakashi Iwai if (client_is_vga(client)) 25579721e0aSTakashi Iwai vgasr_priv.registered_clients++; 2566a9ee8afSDave Airlie 25736704c0cSSeth Forshee if (vga_switcheroo_ready()) { 2589b0be1ebSThierry Reding pr_info("enabled\n"); 2596a9ee8afSDave Airlie vga_switcheroo_enable(); 2606a9ee8afSDave Airlie } 2616a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 2626a9ee8afSDave Airlie return 0; 2636a9ee8afSDave Airlie } 2643e9e63dbSTakashi Iwai 265a645654bSLukas Wunner /** 266a645654bSLukas Wunner * vga_switcheroo_register_client - register vga client 267a645654bSLukas Wunner * @pdev: client pci device 268a645654bSLukas Wunner * @ops: client callbacks 269a645654bSLukas Wunner * @driver_power_control: whether power state is controlled by the driver's 270a645654bSLukas Wunner * runtime pm 271a645654bSLukas Wunner * 272a645654bSLukas Wunner * Register vga client (GPU). Enable vga_switcheroo if another GPU and a 273a645654bSLukas Wunner * handler have already registered. The power state of the client is assumed 274a645654bSLukas Wunner * to be ON. 275a645654bSLukas Wunner * 276a645654bSLukas Wunner * Return: 0 on success, -ENOMEM on memory allocation error. 277a645654bSLukas Wunner */ 2783e9e63dbSTakashi Iwai int vga_switcheroo_register_client(struct pci_dev *pdev, 2790d69704aSDave Airlie const struct vga_switcheroo_client_ops *ops, 2800d69704aSDave Airlie bool driver_power_control) 2813e9e63dbSTakashi Iwai { 28221c5ba8cSLukas Wunner return register_client(pdev, ops, VGA_SWITCHEROO_UNKNOWN_ID, 2837491bfb4SThierry Reding pdev == vga_default_device(), 2847491bfb4SThierry Reding driver_power_control); 2853e9e63dbSTakashi Iwai } 2866a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_register_client); 2876a9ee8afSDave Airlie 288a645654bSLukas Wunner /** 289a645654bSLukas Wunner * vga_switcheroo_register_audio_client - register audio client 290a645654bSLukas Wunner * @pdev: client pci device 291a645654bSLukas Wunner * @ops: client callbacks 292fa3e967fSLukas Wunner * @id: client identifier 293a645654bSLukas Wunner * 294a645654bSLukas Wunner * Register audio client (audio device on a GPU). The power state of the 295a645654bSLukas Wunner * client is assumed to be ON. 296a645654bSLukas Wunner * 297a645654bSLukas Wunner * Return: 0 on success, -ENOMEM on memory allocation error. 298a645654bSLukas Wunner */ 2993e9e63dbSTakashi Iwai int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 3003e9e63dbSTakashi Iwai const struct vga_switcheroo_client_ops *ops, 301fa3e967fSLukas Wunner enum vga_switcheroo_client_id id) 3023e9e63dbSTakashi Iwai { 30321b45676SLukas Wunner return register_client(pdev, ops, id | ID_BIT_AUDIO, false, false); 3043e9e63dbSTakashi Iwai } 3053e9e63dbSTakashi Iwai EXPORT_SYMBOL(vga_switcheroo_register_audio_client); 3063e9e63dbSTakashi Iwai 30779721e0aSTakashi Iwai static struct vga_switcheroo_client * 30879721e0aSTakashi Iwai find_client_from_pci(struct list_head *head, struct pci_dev *pdev) 30979721e0aSTakashi Iwai { 31079721e0aSTakashi Iwai struct vga_switcheroo_client *client; 3117491bfb4SThierry Reding 31279721e0aSTakashi Iwai list_for_each_entry(client, head, list) 31379721e0aSTakashi Iwai if (client->pdev == pdev) 31479721e0aSTakashi Iwai return client; 31579721e0aSTakashi Iwai return NULL; 31679721e0aSTakashi Iwai } 31779721e0aSTakashi Iwai 31879721e0aSTakashi Iwai static struct vga_switcheroo_client * 319fa3e967fSLukas Wunner find_client_from_id(struct list_head *head, 320fa3e967fSLukas Wunner enum vga_switcheroo_client_id client_id) 32179721e0aSTakashi Iwai { 32279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 3237491bfb4SThierry Reding 32479721e0aSTakashi Iwai list_for_each_entry(client, head, list) 32579721e0aSTakashi Iwai if (client->id == client_id) 32679721e0aSTakashi Iwai return client; 32779721e0aSTakashi Iwai return NULL; 32879721e0aSTakashi Iwai } 32979721e0aSTakashi Iwai 33079721e0aSTakashi Iwai static struct vga_switcheroo_client * 33179721e0aSTakashi Iwai find_active_client(struct list_head *head) 33279721e0aSTakashi Iwai { 33379721e0aSTakashi Iwai struct vga_switcheroo_client *client; 3347491bfb4SThierry Reding 33579721e0aSTakashi Iwai list_for_each_entry(client, head, list) 33621b45676SLukas Wunner if (client->active) 33779721e0aSTakashi Iwai return client; 33879721e0aSTakashi Iwai return NULL; 33979721e0aSTakashi Iwai } 34079721e0aSTakashi Iwai 341a645654bSLukas Wunner /** 342a645654bSLukas Wunner * vga_switcheroo_get_client_state() - obtain power state of a given client 343a645654bSLukas Wunner * @pdev: client pci device 344a645654bSLukas Wunner * 345a645654bSLukas Wunner * Obtain power state of a given client as seen from vga_switcheroo. 346a645654bSLukas Wunner * The function is only called from hda_intel.c. 347a645654bSLukas Wunner * 348a645654bSLukas Wunner * Return: Power state. 349a645654bSLukas Wunner */ 350203d027dSLukas Wunner enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *pdev) 351c8e9cf7bSTakashi Iwai { 352c8e9cf7bSTakashi Iwai struct vga_switcheroo_client *client; 3538f12a311SLukas Wunner enum vga_switcheroo_state ret; 354c8e9cf7bSTakashi Iwai 3558f12a311SLukas Wunner mutex_lock(&vgasr_mutex); 356c8e9cf7bSTakashi Iwai client = find_client_from_pci(&vgasr_priv.clients, pdev); 357c8e9cf7bSTakashi Iwai if (!client) 3588f12a311SLukas Wunner ret = VGA_SWITCHEROO_NOT_FOUND; 3598f12a311SLukas Wunner else if (!vgasr_priv.active) 3608f12a311SLukas Wunner ret = VGA_SWITCHEROO_INIT; 3618f12a311SLukas Wunner else 3628f12a311SLukas Wunner ret = client->pwr_state; 3638f12a311SLukas Wunner mutex_unlock(&vgasr_mutex); 3648f12a311SLukas Wunner return ret; 365c8e9cf7bSTakashi Iwai } 366c8e9cf7bSTakashi Iwai EXPORT_SYMBOL(vga_switcheroo_get_client_state); 367c8e9cf7bSTakashi Iwai 368a645654bSLukas Wunner /** 369a645654bSLukas Wunner * vga_switcheroo_unregister_client() - unregister client 370a645654bSLukas Wunner * @pdev: client pci device 371a645654bSLukas Wunner * 372a645654bSLukas Wunner * Unregister client. Disable vga_switcheroo if this is a vga client (GPU). 373a645654bSLukas Wunner */ 3746a9ee8afSDave Airlie void vga_switcheroo_unregister_client(struct pci_dev *pdev) 3756a9ee8afSDave Airlie { 37679721e0aSTakashi Iwai struct vga_switcheroo_client *client; 3776a9ee8afSDave Airlie 3786a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 37979721e0aSTakashi Iwai client = find_client_from_pci(&vgasr_priv.clients, pdev); 38079721e0aSTakashi Iwai if (client) { 3813e9e63dbSTakashi Iwai if (client_is_vga(client)) 3823e9e63dbSTakashi Iwai vgasr_priv.registered_clients--; 38379721e0aSTakashi Iwai list_del(&client->list); 38479721e0aSTakashi Iwai kfree(client); 3856a9ee8afSDave Airlie } 3863e9e63dbSTakashi Iwai if (vgasr_priv.active && vgasr_priv.registered_clients < 2) { 3879b0be1ebSThierry Reding pr_info("disabled\n"); 3886a9ee8afSDave Airlie vga_switcheroo_debugfs_fini(&vgasr_priv); 3896a9ee8afSDave Airlie vgasr_priv.active = false; 3903e9e63dbSTakashi Iwai } 3916a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 3926a9ee8afSDave Airlie } 3936a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_unregister_client); 3946a9ee8afSDave Airlie 395a645654bSLukas Wunner /** 396a645654bSLukas Wunner * vga_switcheroo_client_fb_set() - set framebuffer of a given client 397a645654bSLukas Wunner * @pdev: client pci device 398a645654bSLukas Wunner * @info: framebuffer 399a645654bSLukas Wunner * 400a645654bSLukas Wunner * Set framebuffer of a given client. The console will be remapped to this 401a645654bSLukas Wunner * on switching. 402a645654bSLukas Wunner */ 4036a9ee8afSDave Airlie void vga_switcheroo_client_fb_set(struct pci_dev *pdev, 4046a9ee8afSDave Airlie struct fb_info *info) 4056a9ee8afSDave Airlie { 40679721e0aSTakashi Iwai struct vga_switcheroo_client *client; 4076a9ee8afSDave Airlie 4086a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 40979721e0aSTakashi Iwai client = find_client_from_pci(&vgasr_priv.clients, pdev); 41079721e0aSTakashi Iwai if (client) 41179721e0aSTakashi Iwai client->fb_info = info; 4126a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 4136a9ee8afSDave Airlie } 4146a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_client_fb_set); 4156a9ee8afSDave Airlie 416a645654bSLukas Wunner /** 417a645654bSLukas Wunner * DOC: Manual switching and manual power control 418a645654bSLukas Wunner * 419a645654bSLukas Wunner * In this mode of use, the file /sys/kernel/debug/vgaswitcheroo/switch 420a645654bSLukas Wunner * can be read to retrieve the current vga_switcheroo state and commands 421a645654bSLukas Wunner * can be written to it to change the state. The file appears as soon as 422a645654bSLukas Wunner * two GPU drivers and one handler have registered with vga_switcheroo. 423a645654bSLukas Wunner * The following commands are understood: 424a645654bSLukas Wunner * 425a645654bSLukas Wunner * * OFF: Power off the device not in use. 426a645654bSLukas Wunner * * ON: Power on the device not in use. 427a645654bSLukas Wunner * * IGD: Switch to the integrated graphics device. 428a645654bSLukas Wunner * Power on the integrated GPU if necessary, power off the discrete GPU. 429a645654bSLukas Wunner * Prerequisite is that no user space processes (e.g. Xorg, alsactl) 430a645654bSLukas Wunner * have opened device files of the GPUs or the audio client. If the 431a645654bSLukas Wunner * switch fails, the user may invoke lsof(8) or fuser(1) on /dev/dri/ 432a645654bSLukas Wunner * and /dev/snd/controlC1 to identify processes blocking the switch. 433a645654bSLukas Wunner * * DIS: Switch to the discrete graphics device. 434a645654bSLukas Wunner * * DIGD: Delayed switch to the integrated graphics device. 435a645654bSLukas Wunner * This will perform the switch once the last user space process has 436a645654bSLukas Wunner * closed the device files of the GPUs and the audio client. 437a645654bSLukas Wunner * * DDIS: Delayed switch to the discrete graphics device. 438a645654bSLukas Wunner * * MIGD: Mux-only switch to the integrated graphics device. 439a645654bSLukas Wunner * Does not remap console or change the power state of either gpu. 440a645654bSLukas Wunner * If the integrated GPU is currently off, the screen will turn black. 441a645654bSLukas Wunner * If it is on, the screen will show whatever happens to be in VRAM. 442a645654bSLukas Wunner * Either way, the user has to blindly enter the command to switch back. 443a645654bSLukas Wunner * * MDIS: Mux-only switch to the discrete graphics device. 444a645654bSLukas Wunner * 445a645654bSLukas Wunner * For GPUs whose power state is controlled by the driver's runtime pm, 446a645654bSLukas Wunner * the ON and OFF commands are a no-op (see next section). 447a645654bSLukas Wunner * 448a645654bSLukas Wunner * For muxless machines, the IGD/DIS, DIGD/DDIS and MIGD/MDIS commands 449a645654bSLukas Wunner * should not be used. 450a645654bSLukas Wunner */ 451a645654bSLukas Wunner 4526a9ee8afSDave Airlie static int vga_switcheroo_show(struct seq_file *m, void *v) 4536a9ee8afSDave Airlie { 45479721e0aSTakashi Iwai struct vga_switcheroo_client *client; 45579721e0aSTakashi Iwai int i = 0; 4567491bfb4SThierry Reding 4576a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 45879721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 4590d69704aSDave Airlie seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i, 4607491bfb4SThierry Reding client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : 4617491bfb4SThierry Reding "IGD", 4623e9e63dbSTakashi Iwai client_is_vga(client) ? "" : "-Audio", 46379721e0aSTakashi Iwai client->active ? '+' : ' ', 4640d69704aSDave Airlie client->driver_power_control ? "Dyn" : "", 46579721e0aSTakashi Iwai client->pwr_state ? "Pwr" : "Off", 46679721e0aSTakashi Iwai pci_name(client->pdev)); 46779721e0aSTakashi Iwai i++; 4686a9ee8afSDave Airlie } 4696a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 4706a9ee8afSDave Airlie return 0; 4716a9ee8afSDave Airlie } 4726a9ee8afSDave Airlie 4736a9ee8afSDave Airlie static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file) 4746a9ee8afSDave Airlie { 4756a9ee8afSDave Airlie return single_open(file, vga_switcheroo_show, NULL); 4766a9ee8afSDave Airlie } 4776a9ee8afSDave Airlie 4786a9ee8afSDave Airlie static int vga_switchon(struct vga_switcheroo_client *client) 4796a9ee8afSDave Airlie { 4800d69704aSDave Airlie if (client->driver_power_control) 4810d69704aSDave Airlie return 0; 4825cfb3c3aSDave Airlie if (vgasr_priv.handler->power_state) 4835cfb3c3aSDave Airlie vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); 4846a9ee8afSDave Airlie /* call the driver callback to turn on device */ 48526ec685fSTakashi Iwai client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); 4866a9ee8afSDave Airlie client->pwr_state = VGA_SWITCHEROO_ON; 4876a9ee8afSDave Airlie return 0; 4886a9ee8afSDave Airlie } 4896a9ee8afSDave Airlie 4906a9ee8afSDave Airlie static int vga_switchoff(struct vga_switcheroo_client *client) 4916a9ee8afSDave Airlie { 4920d69704aSDave Airlie if (client->driver_power_control) 4930d69704aSDave Airlie return 0; 4946a9ee8afSDave Airlie /* call the driver callback to turn off device */ 49526ec685fSTakashi Iwai client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); 4965cfb3c3aSDave Airlie if (vgasr_priv.handler->power_state) 4976a9ee8afSDave Airlie vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); 4986a9ee8afSDave Airlie client->pwr_state = VGA_SWITCHEROO_OFF; 4996a9ee8afSDave Airlie return 0; 5006a9ee8afSDave Airlie } 5016a9ee8afSDave Airlie 502fa3e967fSLukas Wunner static void set_audio_state(enum vga_switcheroo_client_id id, 503fa3e967fSLukas Wunner enum vga_switcheroo_state state) 5043e9e63dbSTakashi Iwai { 5053e9e63dbSTakashi Iwai struct vga_switcheroo_client *client; 5063e9e63dbSTakashi Iwai 5073e9e63dbSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO); 5083e9e63dbSTakashi Iwai if (client && client->pwr_state != state) { 5093e9e63dbSTakashi Iwai client->ops->set_gpu_state(client->pdev, state); 5103e9e63dbSTakashi Iwai client->pwr_state = state; 5113e9e63dbSTakashi Iwai } 5123e9e63dbSTakashi Iwai } 5133e9e63dbSTakashi Iwai 51466b37c67SDave Airlie /* stage one happens before delay */ 51566b37c67SDave Airlie static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) 5166a9ee8afSDave Airlie { 51779721e0aSTakashi Iwai struct vga_switcheroo_client *active; 5186a9ee8afSDave Airlie 51979721e0aSTakashi Iwai active = find_active_client(&vgasr_priv.clients); 5206a9ee8afSDave Airlie if (!active) 5216a9ee8afSDave Airlie return 0; 5226a9ee8afSDave Airlie 5236a9ee8afSDave Airlie if (new_client->pwr_state == VGA_SWITCHEROO_OFF) 5246a9ee8afSDave Airlie vga_switchon(new_client); 5256a9ee8afSDave Airlie 5262fbe8c7cSMatthew Garrett vga_set_default_device(new_client->pdev); 52766b37c67SDave Airlie return 0; 52866b37c67SDave Airlie } 52966b37c67SDave Airlie 53066b37c67SDave Airlie /* post delay */ 53166b37c67SDave Airlie static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) 53266b37c67SDave Airlie { 53366b37c67SDave Airlie int ret; 53479721e0aSTakashi Iwai struct vga_switcheroo_client *active; 53566b37c67SDave Airlie 53679721e0aSTakashi Iwai active = find_active_client(&vgasr_priv.clients); 53766b37c67SDave Airlie if (!active) 53866b37c67SDave Airlie return 0; 53966b37c67SDave Airlie 54066b37c67SDave Airlie active->active = false; 5416a9ee8afSDave Airlie 542c91c3faeSTakashi Iwai set_audio_state(active->id, VGA_SWITCHEROO_OFF); 543c91c3faeSTakashi Iwai 5446a9ee8afSDave Airlie if (new_client->fb_info) { 5456a9ee8afSDave Airlie struct fb_event event; 5467491bfb4SThierry Reding 547054430e7SDave Airlie console_lock(); 5486a9ee8afSDave Airlie event.info = new_client->fb_info; 5496a9ee8afSDave Airlie fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); 550054430e7SDave Airlie console_unlock(); 5516a9ee8afSDave Airlie } 5526a9ee8afSDave Airlie 5536a9ee8afSDave Airlie ret = vgasr_priv.handler->switchto(new_client->id); 5546a9ee8afSDave Airlie if (ret) 5556a9ee8afSDave Airlie return ret; 5566a9ee8afSDave Airlie 55726ec685fSTakashi Iwai if (new_client->ops->reprobe) 55826ec685fSTakashi Iwai new_client->ops->reprobe(new_client->pdev); 5598d608aa6SDave Airlie 5606a9ee8afSDave Airlie if (active->pwr_state == VGA_SWITCHEROO_ON) 5616a9ee8afSDave Airlie vga_switchoff(active); 5626a9ee8afSDave Airlie 563c91c3faeSTakashi Iwai set_audio_state(new_client->id, VGA_SWITCHEROO_ON); 564c91c3faeSTakashi Iwai 5656a9ee8afSDave Airlie new_client->active = true; 5666a9ee8afSDave Airlie return 0; 5676a9ee8afSDave Airlie } 5686a9ee8afSDave Airlie 56979721e0aSTakashi Iwai static bool check_can_switch(void) 57079721e0aSTakashi Iwai { 57179721e0aSTakashi Iwai struct vga_switcheroo_client *client; 57279721e0aSTakashi Iwai 57379721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 57426ec685fSTakashi Iwai if (!client->ops->can_switch(client->pdev)) { 5759b0be1ebSThierry Reding pr_err("client %x refused switch\n", client->id); 57679721e0aSTakashi Iwai return false; 57779721e0aSTakashi Iwai } 57879721e0aSTakashi Iwai } 57979721e0aSTakashi Iwai return true; 58079721e0aSTakashi Iwai } 58179721e0aSTakashi Iwai 5826a9ee8afSDave Airlie static ssize_t 5836a9ee8afSDave Airlie vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, 5846a9ee8afSDave Airlie size_t cnt, loff_t *ppos) 5856a9ee8afSDave Airlie { 5866a9ee8afSDave Airlie char usercmd[64]; 58779721e0aSTakashi Iwai int ret; 5886a9ee8afSDave Airlie bool delay = false, can_switch; 589851ab954SDave Airlie bool just_mux = false; 590fa3e967fSLukas Wunner enum vga_switcheroo_client_id client_id = VGA_SWITCHEROO_UNKNOWN_ID; 5916a9ee8afSDave Airlie struct vga_switcheroo_client *client = NULL; 5926a9ee8afSDave Airlie 5936a9ee8afSDave Airlie if (cnt > 63) 5946a9ee8afSDave Airlie cnt = 63; 5956a9ee8afSDave Airlie 5966a9ee8afSDave Airlie if (copy_from_user(usercmd, ubuf, cnt)) 5976a9ee8afSDave Airlie return -EFAULT; 5986a9ee8afSDave Airlie 5996a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 6006a9ee8afSDave Airlie 6018c88e50bSJiri Slaby if (!vgasr_priv.active) { 6028c88e50bSJiri Slaby cnt = -EINVAL; 6038c88e50bSJiri Slaby goto out; 6048c88e50bSJiri Slaby } 6056a9ee8afSDave Airlie 6066a9ee8afSDave Airlie /* pwr off the device not in use */ 6076a9ee8afSDave Airlie if (strncmp(usercmd, "OFF", 3) == 0) { 60879721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 609c91c3faeSTakashi Iwai if (client->active || client_is_audio(client)) 6106a9ee8afSDave Airlie continue; 6110d69704aSDave Airlie if (client->driver_power_control) 6120d69704aSDave Airlie continue; 613c91c3faeSTakashi Iwai set_audio_state(client->id, VGA_SWITCHEROO_OFF); 61479721e0aSTakashi Iwai if (client->pwr_state == VGA_SWITCHEROO_ON) 61579721e0aSTakashi Iwai vga_switchoff(client); 6166a9ee8afSDave Airlie } 6176a9ee8afSDave Airlie goto out; 6186a9ee8afSDave Airlie } 6196a9ee8afSDave Airlie /* pwr on the device not in use */ 6206a9ee8afSDave Airlie if (strncmp(usercmd, "ON", 2) == 0) { 62179721e0aSTakashi Iwai list_for_each_entry(client, &vgasr_priv.clients, list) { 622c91c3faeSTakashi Iwai if (client->active || client_is_audio(client)) 6236a9ee8afSDave Airlie continue; 6240d69704aSDave Airlie if (client->driver_power_control) 6250d69704aSDave Airlie continue; 62679721e0aSTakashi Iwai if (client->pwr_state == VGA_SWITCHEROO_OFF) 62779721e0aSTakashi Iwai vga_switchon(client); 628c91c3faeSTakashi Iwai set_audio_state(client->id, VGA_SWITCHEROO_ON); 6296a9ee8afSDave Airlie } 6306a9ee8afSDave Airlie goto out; 6316a9ee8afSDave Airlie } 6326a9ee8afSDave Airlie 6336a9ee8afSDave Airlie /* request a delayed switch - test can we switch now */ 6346a9ee8afSDave Airlie if (strncmp(usercmd, "DIGD", 4) == 0) { 6356a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_IGD; 6366a9ee8afSDave Airlie delay = true; 6376a9ee8afSDave Airlie } 6386a9ee8afSDave Airlie 6396a9ee8afSDave Airlie if (strncmp(usercmd, "DDIS", 4) == 0) { 6406a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_DIS; 6416a9ee8afSDave Airlie delay = true; 6426a9ee8afSDave Airlie } 6436a9ee8afSDave Airlie 6446a9ee8afSDave Airlie if (strncmp(usercmd, "IGD", 3) == 0) 6456a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_IGD; 6466a9ee8afSDave Airlie 6476a9ee8afSDave Airlie if (strncmp(usercmd, "DIS", 3) == 0) 6486a9ee8afSDave Airlie client_id = VGA_SWITCHEROO_DIS; 6496a9ee8afSDave Airlie 650fea6f330SDan Carpenter if (strncmp(usercmd, "MIGD", 4) == 0) { 651851ab954SDave Airlie just_mux = true; 652851ab954SDave Airlie client_id = VGA_SWITCHEROO_IGD; 653851ab954SDave Airlie } 654fea6f330SDan Carpenter if (strncmp(usercmd, "MDIS", 4) == 0) { 655851ab954SDave Airlie just_mux = true; 656851ab954SDave Airlie client_id = VGA_SWITCHEROO_DIS; 657851ab954SDave Airlie } 658851ab954SDave Airlie 65921c5ba8cSLukas Wunner if (client_id == VGA_SWITCHEROO_UNKNOWN_ID) 6606a9ee8afSDave Airlie goto out; 66179721e0aSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, client_id); 66279721e0aSTakashi Iwai if (!client) 66379721e0aSTakashi Iwai goto out; 6646a9ee8afSDave Airlie 6656a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = false; 666851ab954SDave Airlie 667851ab954SDave Airlie if (just_mux) { 668851ab954SDave Airlie ret = vgasr_priv.handler->switchto(client_id); 669851ab954SDave Airlie goto out; 670851ab954SDave Airlie } 671851ab954SDave Airlie 67279721e0aSTakashi Iwai if (client->active) 673a67b8887SFlorian Mickler goto out; 674a67b8887SFlorian Mickler 6756a9ee8afSDave Airlie /* okay we want a switch - test if devices are willing to switch */ 67679721e0aSTakashi Iwai can_switch = check_can_switch(); 6776a9ee8afSDave Airlie 6786a9ee8afSDave Airlie if (can_switch == false && delay == false) 6796a9ee8afSDave Airlie goto out; 6806a9ee8afSDave Airlie 68179721e0aSTakashi Iwai if (can_switch) { 68266b37c67SDave Airlie ret = vga_switchto_stage1(client); 6836a9ee8afSDave Airlie if (ret) 6849b0be1ebSThierry Reding pr_err("switching failed stage 1 %d\n", ret); 68566b37c67SDave Airlie 68666b37c67SDave Airlie ret = vga_switchto_stage2(client); 68766b37c67SDave Airlie if (ret) 6889b0be1ebSThierry Reding pr_err("switching failed stage 2 %d\n", ret); 68966b37c67SDave Airlie 6906a9ee8afSDave Airlie } else { 6919b0be1ebSThierry Reding pr_info("setting delayed switch to client %d\n", client->id); 6926a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = true; 6936a9ee8afSDave Airlie vgasr_priv.delayed_client_id = client_id; 6946a9ee8afSDave Airlie 69566b37c67SDave Airlie ret = vga_switchto_stage1(client); 69666b37c67SDave Airlie if (ret) 6979b0be1ebSThierry Reding pr_err("delayed switching stage 1 failed %d\n", ret); 6986a9ee8afSDave Airlie } 6996a9ee8afSDave Airlie 7006a9ee8afSDave Airlie out: 7016a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 7026a9ee8afSDave Airlie return cnt; 7036a9ee8afSDave Airlie } 7046a9ee8afSDave Airlie 7056a9ee8afSDave Airlie static const struct file_operations vga_switcheroo_debugfs_fops = { 7066a9ee8afSDave Airlie .owner = THIS_MODULE, 7076a9ee8afSDave Airlie .open = vga_switcheroo_debugfs_open, 7086a9ee8afSDave Airlie .write = vga_switcheroo_debugfs_write, 7096a9ee8afSDave Airlie .read = seq_read, 7106a9ee8afSDave Airlie .llseek = seq_lseek, 7116a9ee8afSDave Airlie .release = single_release, 7126a9ee8afSDave Airlie }; 7136a9ee8afSDave Airlie 7146a9ee8afSDave Airlie static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv) 7156a9ee8afSDave Airlie { 7166a9ee8afSDave Airlie debugfs_remove(priv->switch_file); 7176a9ee8afSDave Airlie priv->switch_file = NULL; 718798ae0f6SThierry Reding 7196a9ee8afSDave Airlie debugfs_remove(priv->debugfs_root); 7206a9ee8afSDave Airlie priv->debugfs_root = NULL; 7216a9ee8afSDave Airlie } 7226a9ee8afSDave Airlie 7236a9ee8afSDave Airlie static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv) 7246a9ee8afSDave Airlie { 7259b0be1ebSThierry Reding static const char mp[] = "/sys/kernel/debug"; 7269b0be1ebSThierry Reding 7276a9ee8afSDave Airlie /* already initialised */ 7286a9ee8afSDave Airlie if (priv->debugfs_root) 7296a9ee8afSDave Airlie return 0; 7306a9ee8afSDave Airlie priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL); 7316a9ee8afSDave Airlie 7326a9ee8afSDave Airlie if (!priv->debugfs_root) { 7339b0be1ebSThierry Reding pr_err("Cannot create %s/vgaswitcheroo\n", mp); 7346a9ee8afSDave Airlie goto fail; 7356a9ee8afSDave Airlie } 7366a9ee8afSDave Airlie 7376a9ee8afSDave Airlie priv->switch_file = debugfs_create_file("switch", 0644, 7387491bfb4SThierry Reding priv->debugfs_root, NULL, 7397491bfb4SThierry Reding &vga_switcheroo_debugfs_fops); 7406a9ee8afSDave Airlie if (!priv->switch_file) { 7419b0be1ebSThierry Reding pr_err("cannot create %s/vgaswitcheroo/switch\n", mp); 7426a9ee8afSDave Airlie goto fail; 7436a9ee8afSDave Airlie } 7446a9ee8afSDave Airlie return 0; 7456a9ee8afSDave Airlie fail: 7466a9ee8afSDave Airlie vga_switcheroo_debugfs_fini(priv); 7476a9ee8afSDave Airlie return -1; 7486a9ee8afSDave Airlie } 7496a9ee8afSDave Airlie 750a645654bSLukas Wunner /** 751a645654bSLukas Wunner * vga_switcheroo_process_delayed_switch() - helper for delayed switching 752a645654bSLukas Wunner * 753a645654bSLukas Wunner * Process a delayed switch if one is pending. DRM drivers should call this 754a645654bSLukas Wunner * from their ->lastclose callback. 755a645654bSLukas Wunner * 756a645654bSLukas Wunner * Return: 0 on success. -EINVAL if no delayed switch is pending, if the client 757a645654bSLukas Wunner * has unregistered in the meantime or if there are other clients blocking the 758a645654bSLukas Wunner * switch. If the actual switch fails, an error is reported and 0 is returned. 759a645654bSLukas Wunner */ 7606a9ee8afSDave Airlie int vga_switcheroo_process_delayed_switch(void) 7616a9ee8afSDave Airlie { 76279721e0aSTakashi Iwai struct vga_switcheroo_client *client; 7636a9ee8afSDave Airlie int ret; 7646a9ee8afSDave Airlie int err = -EINVAL; 7656a9ee8afSDave Airlie 7666a9ee8afSDave Airlie mutex_lock(&vgasr_mutex); 7676a9ee8afSDave Airlie if (!vgasr_priv.delayed_switch_active) 7686a9ee8afSDave Airlie goto err; 7696a9ee8afSDave Airlie 7709b0be1ebSThierry Reding pr_info("processing delayed switch to %d\n", 7719b0be1ebSThierry Reding vgasr_priv.delayed_client_id); 7726a9ee8afSDave Airlie 77379721e0aSTakashi Iwai client = find_client_from_id(&vgasr_priv.clients, 77479721e0aSTakashi Iwai vgasr_priv.delayed_client_id); 77579721e0aSTakashi Iwai if (!client || !check_can_switch()) 7766a9ee8afSDave Airlie goto err; 7776a9ee8afSDave Airlie 77866b37c67SDave Airlie ret = vga_switchto_stage2(client); 7796a9ee8afSDave Airlie if (ret) 7809b0be1ebSThierry Reding pr_err("delayed switching failed stage 2 %d\n", ret); 7816a9ee8afSDave Airlie 7826a9ee8afSDave Airlie vgasr_priv.delayed_switch_active = false; 7836a9ee8afSDave Airlie err = 0; 7846a9ee8afSDave Airlie err: 7856a9ee8afSDave Airlie mutex_unlock(&vgasr_mutex); 7866a9ee8afSDave Airlie return err; 7876a9ee8afSDave Airlie } 7886a9ee8afSDave Airlie EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); 7890d69704aSDave Airlie 790a645654bSLukas Wunner /** 791a645654bSLukas Wunner * DOC: Driver power control 792a645654bSLukas Wunner * 793a645654bSLukas Wunner * In this mode of use, the discrete GPU automatically powers up and down at 794a645654bSLukas Wunner * the discretion of the driver's runtime pm. On muxed machines, the user may 795a645654bSLukas Wunner * still influence the muxer state by way of the debugfs interface, however 796a645654bSLukas Wunner * the ON and OFF commands become a no-op for the discrete GPU. 797a645654bSLukas Wunner * 798a645654bSLukas Wunner * This mode is the default on Nvidia HybridPower/Optimus and ATI PowerXpress. 799a645654bSLukas Wunner * Specifying nouveau.runpm=0, radeon.runpm=0 or amdgpu.runpm=0 on the kernel 800a645654bSLukas Wunner * command line disables it. 801a645654bSLukas Wunner * 802a645654bSLukas Wunner * When the driver decides to power up or down, it notifies vga_switcheroo 803a645654bSLukas Wunner * thereof so that it can (a) power the audio device on the GPU up or down, 804a645654bSLukas Wunner * and (b) update its internal power state representation for the device. 805a645654bSLukas Wunner * This is achieved by vga_switcheroo_set_dynamic_switch(). 806a645654bSLukas Wunner * 807a645654bSLukas Wunner * After the GPU has been suspended, the handler needs to be called to cut 808a645654bSLukas Wunner * power to the GPU. Likewise it needs to reinstate power before the GPU 809a645654bSLukas Wunner * can resume. This is achieved by vga_switcheroo_init_domain_pm_ops(), 810a645654bSLukas Wunner * which augments the GPU's suspend/resume functions by the requisite 811a645654bSLukas Wunner * calls to the handler. 812a645654bSLukas Wunner * 813a645654bSLukas Wunner * When the audio device resumes, the GPU needs to be woken. This is achieved 814a645654bSLukas Wunner * by vga_switcheroo_init_domain_pm_optimus_hdmi_audio(), which augments the 815a645654bSLukas Wunner * audio device's resume function. 816a645654bSLukas Wunner * 817a645654bSLukas Wunner * On muxed machines, if the mux is initially switched to the discrete GPU, 818a645654bSLukas Wunner * the user ends up with a black screen when the GPU powers down after boot. 819a645654bSLukas Wunner * As a workaround, the mux is forced to the integrated GPU on runtime suspend, 820a645654bSLukas Wunner * cf. https://bugs.freedesktop.org/show_bug.cgi?id=75917 821a645654bSLukas Wunner */ 822a645654bSLukas Wunner 8237491bfb4SThierry Reding static void vga_switcheroo_power_switch(struct pci_dev *pdev, 8247491bfb4SThierry Reding enum vga_switcheroo_state state) 8250d69704aSDave Airlie { 8260d69704aSDave Airlie struct vga_switcheroo_client *client; 8270d69704aSDave Airlie 8280d69704aSDave Airlie if (!vgasr_priv.handler->power_state) 8290d69704aSDave Airlie return; 8300d69704aSDave Airlie 8310d69704aSDave Airlie client = find_client_from_pci(&vgasr_priv.clients, pdev); 8320d69704aSDave Airlie if (!client) 8330d69704aSDave Airlie return; 8340d69704aSDave Airlie 8350d69704aSDave Airlie if (!client->driver_power_control) 8360d69704aSDave Airlie return; 8370d69704aSDave Airlie 8380d69704aSDave Airlie vgasr_priv.handler->power_state(client->id, state); 8390d69704aSDave Airlie } 8400d69704aSDave Airlie 841a645654bSLukas Wunner /** 842a645654bSLukas Wunner * vga_switcheroo_set_dynamic_switch() - helper for driver power control 843a645654bSLukas Wunner * @pdev: client pci device 844a645654bSLukas Wunner * @dynamic: new power state 845a645654bSLukas Wunner * 846a645654bSLukas Wunner * Helper for GPUs whose power state is controlled by the driver's runtime pm. 847a645654bSLukas Wunner * When the driver decides to power up or down, it notifies vga_switcheroo 848a645654bSLukas Wunner * thereof using this helper so that it can (a) power the audio device on 849a645654bSLukas Wunner * the GPU up or down, and (b) update its internal power state representation 850a645654bSLukas Wunner * for the device. 851a645654bSLukas Wunner */ 8527491bfb4SThierry Reding void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, 8537491bfb4SThierry Reding enum vga_switcheroo_state dynamic) 8540d69704aSDave Airlie { 8550d69704aSDave Airlie struct vga_switcheroo_client *client; 8560d69704aSDave Airlie 8578f12a311SLukas Wunner mutex_lock(&vgasr_mutex); 8580d69704aSDave Airlie client = find_client_from_pci(&vgasr_priv.clients, pdev); 8598f12a311SLukas Wunner if (!client || !client->driver_power_control) { 8608f12a311SLukas Wunner mutex_unlock(&vgasr_mutex); 8610d69704aSDave Airlie return; 8628f12a311SLukas Wunner } 8630d69704aSDave Airlie 8640d69704aSDave Airlie client->pwr_state = dynamic; 8650d69704aSDave Airlie set_audio_state(client->id, dynamic); 8668f12a311SLukas Wunner mutex_unlock(&vgasr_mutex); 8670d69704aSDave Airlie } 8680d69704aSDave Airlie EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch); 8690d69704aSDave Airlie 8700d69704aSDave Airlie /* switcheroo power domain */ 8710d69704aSDave Airlie static int vga_switcheroo_runtime_suspend(struct device *dev) 8720d69704aSDave Airlie { 8730d69704aSDave Airlie struct pci_dev *pdev = to_pci_dev(dev); 8740d69704aSDave Airlie int ret; 8750d69704aSDave Airlie 8760d69704aSDave Airlie ret = dev->bus->pm->runtime_suspend(dev); 8770d69704aSDave Airlie if (ret) 8780d69704aSDave Airlie return ret; 8798f12a311SLukas Wunner mutex_lock(&vgasr_mutex); 880f2bc5616SAlex Deucher if (vgasr_priv.handler->switchto) 881f2bc5616SAlex Deucher vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD); 8820d69704aSDave Airlie vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF); 8838f12a311SLukas Wunner mutex_unlock(&vgasr_mutex); 8840d69704aSDave Airlie return 0; 8850d69704aSDave Airlie } 8860d69704aSDave Airlie 8870d69704aSDave Airlie static int vga_switcheroo_runtime_resume(struct device *dev) 8880d69704aSDave Airlie { 8890d69704aSDave Airlie struct pci_dev *pdev = to_pci_dev(dev); 8900d69704aSDave Airlie int ret; 8910d69704aSDave Airlie 8928f12a311SLukas Wunner mutex_lock(&vgasr_mutex); 8930d69704aSDave Airlie vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_ON); 8948f12a311SLukas Wunner mutex_unlock(&vgasr_mutex); 8950d69704aSDave Airlie ret = dev->bus->pm->runtime_resume(dev); 8960d69704aSDave Airlie if (ret) 8970d69704aSDave Airlie return ret; 8980d69704aSDave Airlie 8990d69704aSDave Airlie return 0; 9000d69704aSDave Airlie } 9010d69704aSDave Airlie 902a645654bSLukas Wunner /** 903a645654bSLukas Wunner * vga_switcheroo_init_domain_pm_ops() - helper for driver power control 904a645654bSLukas Wunner * @dev: vga client device 905a645654bSLukas Wunner * @domain: power domain 906a645654bSLukas Wunner * 907a645654bSLukas Wunner * Helper for GPUs whose power state is controlled by the driver's runtime pm. 908a645654bSLukas Wunner * After the GPU has been suspended, the handler needs to be called to cut 909a645654bSLukas Wunner * power to the GPU. Likewise it needs to reinstate power before the GPU 910a645654bSLukas Wunner * can resume. To this end, this helper augments the suspend/resume functions 911a645654bSLukas Wunner * by the requisite calls to the handler. It needs only be called on platforms 912a645654bSLukas Wunner * where the power switch is separate to the device being powered down. 913a645654bSLukas Wunner */ 9147491bfb4SThierry Reding int vga_switcheroo_init_domain_pm_ops(struct device *dev, 9157491bfb4SThierry Reding struct dev_pm_domain *domain) 9160d69704aSDave Airlie { 9170d69704aSDave Airlie /* copy over all the bus versions */ 9180d69704aSDave Airlie if (dev->bus && dev->bus->pm) { 9190d69704aSDave Airlie domain->ops = *dev->bus->pm; 9200d69704aSDave Airlie domain->ops.runtime_suspend = vga_switcheroo_runtime_suspend; 9210d69704aSDave Airlie domain->ops.runtime_resume = vga_switcheroo_runtime_resume; 9220d69704aSDave Airlie 9230d69704aSDave Airlie dev->pm_domain = domain; 9240d69704aSDave Airlie return 0; 9250d69704aSDave Airlie } 9260d69704aSDave Airlie dev->pm_domain = NULL; 9270d69704aSDave Airlie return -EINVAL; 9280d69704aSDave Airlie } 9290d69704aSDave Airlie EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops); 9300d69704aSDave Airlie 931766a53d0SAlex Deucher void vga_switcheroo_fini_domain_pm_ops(struct device *dev) 932766a53d0SAlex Deucher { 933766a53d0SAlex Deucher dev->pm_domain = NULL; 934766a53d0SAlex Deucher } 935766a53d0SAlex Deucher EXPORT_SYMBOL(vga_switcheroo_fini_domain_pm_ops); 936766a53d0SAlex Deucher 9370d69704aSDave Airlie static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) 9380d69704aSDave Airlie { 9390d69704aSDave Airlie struct pci_dev *pdev = to_pci_dev(dev); 9408f12a311SLukas Wunner struct vga_switcheroo_client *client; 9418f12a311SLukas Wunner struct device *video_dev = NULL; 9420d69704aSDave Airlie int ret; 9430d69704aSDave Airlie 9440d69704aSDave Airlie /* we need to check if we have to switch back on the video 9450d69704aSDave Airlie device so the audio device can come back */ 9468f12a311SLukas Wunner mutex_lock(&vgasr_mutex); 9470d69704aSDave Airlie list_for_each_entry(client, &vgasr_priv.clients, list) { 9487491bfb4SThierry Reding if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && 9497491bfb4SThierry Reding client_is_vga(client)) { 9508f12a311SLukas Wunner video_dev = &client->pdev->dev; 9510d69704aSDave Airlie break; 9520d69704aSDave Airlie } 9530d69704aSDave Airlie } 9548f12a311SLukas Wunner mutex_unlock(&vgasr_mutex); 9558f12a311SLukas Wunner 9568f12a311SLukas Wunner if (video_dev) { 9578f12a311SLukas Wunner ret = pm_runtime_get_sync(video_dev); 9588f12a311SLukas Wunner if (ret && ret != 1) 9598f12a311SLukas Wunner return ret; 9608f12a311SLukas Wunner } 9610d69704aSDave Airlie ret = dev->bus->pm->runtime_resume(dev); 9620d69704aSDave Airlie 9630d69704aSDave Airlie /* put the reference for the gpu */ 9648f12a311SLukas Wunner if (video_dev) { 9658f12a311SLukas Wunner pm_runtime_mark_last_busy(video_dev); 9668f12a311SLukas Wunner pm_runtime_put_autosuspend(video_dev); 9670d69704aSDave Airlie } 9680d69704aSDave Airlie return ret; 9690d69704aSDave Airlie } 9700d69704aSDave Airlie 971a645654bSLukas Wunner /** 972a645654bSLukas Wunner * vga_switcheroo_init_domain_pm_optimus_hdmi_audio() - helper for driver 973a645654bSLukas Wunner * power control 974a645654bSLukas Wunner * @dev: audio client device 975a645654bSLukas Wunner * @domain: power domain 976a645654bSLukas Wunner * 977a645654bSLukas Wunner * Helper for GPUs whose power state is controlled by the driver's runtime pm. 978a645654bSLukas Wunner * When the audio device resumes, the GPU needs to be woken. This helper 979a645654bSLukas Wunner * augments the audio device's resume function to do that. 980a645654bSLukas Wunner * 981a645654bSLukas Wunner * Return: 0 on success, -EINVAL if no power management operations are 982a645654bSLukas Wunner * defined for this device. 983a645654bSLukas Wunner */ 9847491bfb4SThierry Reding int 9857491bfb4SThierry Reding vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, 9867491bfb4SThierry Reding struct dev_pm_domain *domain) 9870d69704aSDave Airlie { 9880d69704aSDave Airlie /* copy over all the bus versions */ 9890d69704aSDave Airlie if (dev->bus && dev->bus->pm) { 9900d69704aSDave Airlie domain->ops = *dev->bus->pm; 9917491bfb4SThierry Reding domain->ops.runtime_resume = 9927491bfb4SThierry Reding vga_switcheroo_runtime_resume_hdmi_audio; 9930d69704aSDave Airlie 9940d69704aSDave Airlie dev->pm_domain = domain; 9950d69704aSDave Airlie return 0; 9960d69704aSDave Airlie } 9970d69704aSDave Airlie dev->pm_domain = NULL; 9980d69704aSDave Airlie return -EINVAL; 9990d69704aSDave Airlie } 10000d69704aSDave Airlie EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio); 1001