Path: blob/master/drivers/gpu/vga/vga_switcheroo.c
15111 views
/*1* Copyright (c) 2010 Red Hat Inc.2* Author : Dave Airlie <[email protected]>3*4*5* Licensed under GPLv26*7* vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs89Switcher interface - methods require for ATPX and DCM10- switchto - this throws the output MUX switch11- discrete_set_power - sets the power state for the discrete card1213GPU driver interface14- set_gpu_state - this should do the equiv of s/r for the card15- this should *not* set the discrete power state16- switch_check - check if the device is in a position to switch now17*/1819#include <linux/module.h>20#include <linux/dmi.h>21#include <linux/seq_file.h>22#include <linux/uaccess.h>23#include <linux/fs.h>24#include <linux/debugfs.h>25#include <linux/fb.h>2627#include <linux/pci.h>28#include <linux/vga_switcheroo.h>2930struct vga_switcheroo_client {31struct pci_dev *pdev;32struct fb_info *fb_info;33int pwr_state;34void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state);35void (*reprobe)(struct pci_dev *pdev);36bool (*can_switch)(struct pci_dev *pdev);37int id;38bool active;39};4041static DEFINE_MUTEX(vgasr_mutex);4243struct vgasr_priv {4445bool active;46bool delayed_switch_active;47enum vga_switcheroo_client_id delayed_client_id;4849struct dentry *debugfs_root;50struct dentry *switch_file;5152int registered_clients;53struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS];5455struct vga_switcheroo_handler *handler;56};5758static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv);59static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);6061/* only one switcheroo per system */62static struct vgasr_priv vgasr_priv;6364int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)65{66mutex_lock(&vgasr_mutex);67if (vgasr_priv.handler) {68mutex_unlock(&vgasr_mutex);69return -EINVAL;70}7172vgasr_priv.handler = handler;73mutex_unlock(&vgasr_mutex);74return 0;75}76EXPORT_SYMBOL(vga_switcheroo_register_handler);7778void vga_switcheroo_unregister_handler(void)79{80mutex_lock(&vgasr_mutex);81vgasr_priv.handler = NULL;82mutex_unlock(&vgasr_mutex);83}84EXPORT_SYMBOL(vga_switcheroo_unregister_handler);8586static void vga_switcheroo_enable(void)87{88int i;89int ret;90/* call the handler to init */91vgasr_priv.handler->init();9293for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {94ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev);95if (ret < 0)96return;9798vgasr_priv.clients[i].id = ret;99}100vga_switcheroo_debugfs_init(&vgasr_priv);101vgasr_priv.active = true;102}103104int vga_switcheroo_register_client(struct pci_dev *pdev,105void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state),106void (*reprobe)(struct pci_dev *pdev),107bool (*can_switch)(struct pci_dev *pdev))108{109int index;110111mutex_lock(&vgasr_mutex);112/* don't do IGD vs DIS here */113if (vgasr_priv.registered_clients & 1)114index = 1;115else116index = 0;117118vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON;119vgasr_priv.clients[index].pdev = pdev;120vgasr_priv.clients[index].set_gpu_state = set_gpu_state;121vgasr_priv.clients[index].reprobe = reprobe;122vgasr_priv.clients[index].can_switch = can_switch;123vgasr_priv.clients[index].id = -1;124if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)125vgasr_priv.clients[index].active = true;126127vgasr_priv.registered_clients |= (1 << index);128129/* if we get two clients + handler */130if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) {131printk(KERN_INFO "vga_switcheroo: enabled\n");132vga_switcheroo_enable();133}134mutex_unlock(&vgasr_mutex);135return 0;136}137EXPORT_SYMBOL(vga_switcheroo_register_client);138139void vga_switcheroo_unregister_client(struct pci_dev *pdev)140{141int i;142143mutex_lock(&vgasr_mutex);144for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {145if (vgasr_priv.clients[i].pdev == pdev) {146vgasr_priv.registered_clients &= ~(1 << i);147break;148}149}150151printk(KERN_INFO "vga_switcheroo: disabled\n");152vga_switcheroo_debugfs_fini(&vgasr_priv);153vgasr_priv.active = false;154mutex_unlock(&vgasr_mutex);155}156EXPORT_SYMBOL(vga_switcheroo_unregister_client);157158void vga_switcheroo_client_fb_set(struct pci_dev *pdev,159struct fb_info *info)160{161int i;162163mutex_lock(&vgasr_mutex);164for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {165if (vgasr_priv.clients[i].pdev == pdev) {166vgasr_priv.clients[i].fb_info = info;167break;168}169}170mutex_unlock(&vgasr_mutex);171}172EXPORT_SYMBOL(vga_switcheroo_client_fb_set);173174static int vga_switcheroo_show(struct seq_file *m, void *v)175{176int i;177mutex_lock(&vgasr_mutex);178for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {179seq_printf(m, "%d:%s:%c:%s:%s\n", i,180vgasr_priv.clients[i].id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD",181vgasr_priv.clients[i].active ? '+' : ' ',182vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off",183pci_name(vgasr_priv.clients[i].pdev));184}185mutex_unlock(&vgasr_mutex);186return 0;187}188189static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)190{191return single_open(file, vga_switcheroo_show, NULL);192}193194static int vga_switchon(struct vga_switcheroo_client *client)195{196if (vgasr_priv.handler->power_state)197vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);198/* call the driver callback to turn on device */199client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON);200client->pwr_state = VGA_SWITCHEROO_ON;201return 0;202}203204static int vga_switchoff(struct vga_switcheroo_client *client)205{206/* call the driver callback to turn off device */207client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);208if (vgasr_priv.handler->power_state)209vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF);210client->pwr_state = VGA_SWITCHEROO_OFF;211return 0;212}213214/* stage one happens before delay */215static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)216{217int i;218struct vga_switcheroo_client *active = NULL;219220for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {221if (vgasr_priv.clients[i].active == true) {222active = &vgasr_priv.clients[i];223break;224}225}226if (!active)227return 0;228229if (new_client->pwr_state == VGA_SWITCHEROO_OFF)230vga_switchon(new_client);231232/* swap shadow resource to denote boot VGA device has changed so X starts on new device */233active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW;234new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;235return 0;236}237238/* post delay */239static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)240{241int ret;242int i;243struct vga_switcheroo_client *active = NULL;244245for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {246if (vgasr_priv.clients[i].active == true) {247active = &vgasr_priv.clients[i];248break;249}250}251if (!active)252return 0;253254active->active = false;255256if (new_client->fb_info) {257struct fb_event event;258event.info = new_client->fb_info;259fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);260}261262ret = vgasr_priv.handler->switchto(new_client->id);263if (ret)264return ret;265266if (new_client->reprobe)267new_client->reprobe(new_client->pdev);268269if (active->pwr_state == VGA_SWITCHEROO_ON)270vga_switchoff(active);271272new_client->active = true;273return 0;274}275276static ssize_t277vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,278size_t cnt, loff_t *ppos)279{280char usercmd[64];281const char *pdev_name;282int i, ret;283bool delay = false, can_switch;284bool just_mux = false;285int client_id = -1;286struct vga_switcheroo_client *client = NULL;287288if (cnt > 63)289cnt = 63;290291if (copy_from_user(usercmd, ubuf, cnt))292return -EFAULT;293294mutex_lock(&vgasr_mutex);295296if (!vgasr_priv.active) {297cnt = -EINVAL;298goto out;299}300301/* pwr off the device not in use */302if (strncmp(usercmd, "OFF", 3) == 0) {303for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {304if (vgasr_priv.clients[i].active)305continue;306if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON)307vga_switchoff(&vgasr_priv.clients[i]);308}309goto out;310}311/* pwr on the device not in use */312if (strncmp(usercmd, "ON", 2) == 0) {313for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {314if (vgasr_priv.clients[i].active)315continue;316if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF)317vga_switchon(&vgasr_priv.clients[i]);318}319goto out;320}321322/* request a delayed switch - test can we switch now */323if (strncmp(usercmd, "DIGD", 4) == 0) {324client_id = VGA_SWITCHEROO_IGD;325delay = true;326}327328if (strncmp(usercmd, "DDIS", 4) == 0) {329client_id = VGA_SWITCHEROO_DIS;330delay = true;331}332333if (strncmp(usercmd, "IGD", 3) == 0)334client_id = VGA_SWITCHEROO_IGD;335336if (strncmp(usercmd, "DIS", 3) == 0)337client_id = VGA_SWITCHEROO_DIS;338339if (strncmp(usercmd, "MIGD", 4) == 0) {340just_mux = true;341client_id = VGA_SWITCHEROO_IGD;342}343if (strncmp(usercmd, "MDIS", 4) == 0) {344just_mux = true;345client_id = VGA_SWITCHEROO_DIS;346}347348if (client_id == -1)349goto out;350351for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {352if (vgasr_priv.clients[i].id == client_id) {353client = &vgasr_priv.clients[i];354break;355}356}357358vgasr_priv.delayed_switch_active = false;359360if (just_mux) {361ret = vgasr_priv.handler->switchto(client_id);362goto out;363}364365if (client->active == true)366goto out;367368/* okay we want a switch - test if devices are willing to switch */369can_switch = true;370for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {371can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);372if (can_switch == false) {373printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i);374break;375}376}377378if (can_switch == false && delay == false)379goto out;380381if (can_switch == true) {382pdev_name = pci_name(client->pdev);383ret = vga_switchto_stage1(client);384if (ret)385printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret);386387ret = vga_switchto_stage2(client);388if (ret)389printk(KERN_ERR "vga_switcheroo: switching failed stage 2 %d\n", ret);390391} else {392printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id);393vgasr_priv.delayed_switch_active = true;394vgasr_priv.delayed_client_id = client_id;395396ret = vga_switchto_stage1(client);397if (ret)398printk(KERN_ERR "vga_switcheroo: delayed switching stage 1 failed %d\n", ret);399}400401out:402mutex_unlock(&vgasr_mutex);403return cnt;404}405406static const struct file_operations vga_switcheroo_debugfs_fops = {407.owner = THIS_MODULE,408.open = vga_switcheroo_debugfs_open,409.write = vga_switcheroo_debugfs_write,410.read = seq_read,411.llseek = seq_lseek,412.release = single_release,413};414415static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv)416{417if (priv->switch_file) {418debugfs_remove(priv->switch_file);419priv->switch_file = NULL;420}421if (priv->debugfs_root) {422debugfs_remove(priv->debugfs_root);423priv->debugfs_root = NULL;424}425}426427static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv)428{429/* already initialised */430if (priv->debugfs_root)431return 0;432priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL);433434if (!priv->debugfs_root) {435printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n");436goto fail;437}438439priv->switch_file = debugfs_create_file("switch", 0644,440priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops);441if (!priv->switch_file) {442printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n");443goto fail;444}445return 0;446fail:447vga_switcheroo_debugfs_fini(priv);448return -1;449}450451int vga_switcheroo_process_delayed_switch(void)452{453struct vga_switcheroo_client *client = NULL;454const char *pdev_name;455bool can_switch = true;456int i;457int ret;458int err = -EINVAL;459460mutex_lock(&vgasr_mutex);461if (!vgasr_priv.delayed_switch_active)462goto err;463464printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id);465466for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {467if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id)468client = &vgasr_priv.clients[i];469can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);470if (can_switch == false) {471printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i);472break;473}474}475476if (can_switch == false || client == NULL)477goto err;478479pdev_name = pci_name(client->pdev);480ret = vga_switchto_stage2(client);481if (ret)482printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret);483484vgasr_priv.delayed_switch_active = false;485err = 0;486err:487mutex_unlock(&vgasr_mutex);488return err;489}490EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);491492493494