diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
| commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
| tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /drivers/gpu/vga | |
| parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) | |
Patched in Tegra support.
Diffstat (limited to 'drivers/gpu/vga')
| -rw-r--r-- | drivers/gpu/vga/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/gpu/vga/vga_switcheroo.c | 348 | ||||
| -rw-r--r-- | drivers/gpu/vga/vgaarb.c | 98 |
3 files changed, 185 insertions, 264 deletions
diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig index 29437eabe09..96c83a9a76b 100644 --- a/drivers/gpu/vga/Kconfig +++ b/drivers/gpu/vga/Kconfig | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | config VGA_ARB | 1 | config VGA_ARB |
| 2 | bool "VGA Arbitration" if EXPERT | 2 | bool "VGA Arbitration" if EXPERT |
| 3 | default y | 3 | default y |
| 4 | depends on (PCI && !S390) | 4 | depends on PCI |
| 5 | help | 5 | help |
| 6 | Some "legacy" VGA devices implemented on PCI typically have the same | 6 | Some "legacy" VGA devices implemented on PCI typically have the same |
| 7 | hard-decoded addresses as they did on ISA. When multiple PCI devices | 7 | hard-decoded addresses as they did on ISA. When multiple PCI devices |
| @@ -21,7 +21,6 @@ config VGA_SWITCHEROO | |||
| 21 | bool "Laptop Hybrid Graphics - GPU switching support" | 21 | bool "Laptop Hybrid Graphics - GPU switching support" |
| 22 | depends on X86 | 22 | depends on X86 |
| 23 | depends on ACPI | 23 | depends on ACPI |
| 24 | select VGA_ARB | ||
| 25 | help | 24 | help |
| 26 | Many laptops released in 2008/9/10 have two GPUs with a multiplexer | 25 | Many laptops released in 2008/9/10 have two GPUs with a multiplexer |
| 27 | to switch between them. This adds support for dynamic switching when | 26 | to switch between them. This adds support for dynamic switching when |
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index fa60add0ff6..58434e804d9 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | */ | 18 | */ |
| 19 | 19 | ||
| 20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 21 | #include <linux/dmi.h> | ||
| 21 | #include <linux/seq_file.h> | 22 | #include <linux/seq_file.h> |
| 22 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
| 23 | #include <linux/fs.h> | 24 | #include <linux/fs.h> |
| @@ -27,16 +28,15 @@ | |||
| 27 | #include <linux/pci.h> | 28 | #include <linux/pci.h> |
| 28 | #include <linux/vga_switcheroo.h> | 29 | #include <linux/vga_switcheroo.h> |
| 29 | 30 | ||
| 30 | #include <linux/vgaarb.h> | ||
| 31 | |||
| 32 | struct vga_switcheroo_client { | 31 | struct vga_switcheroo_client { |
| 33 | struct pci_dev *pdev; | 32 | struct pci_dev *pdev; |
| 34 | struct fb_info *fb_info; | 33 | struct fb_info *fb_info; |
| 35 | int pwr_state; | 34 | int pwr_state; |
| 36 | const struct vga_switcheroo_client_ops *ops; | 35 | void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state); |
| 36 | void (*reprobe)(struct pci_dev *pdev); | ||
| 37 | bool (*can_switch)(struct pci_dev *pdev); | ||
| 37 | int id; | 38 | int id; |
| 38 | bool active; | 39 | bool active; |
| 39 | struct list_head list; | ||
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | static DEFINE_MUTEX(vgasr_mutex); | 42 | static DEFINE_MUTEX(vgasr_mutex); |
| @@ -51,52 +51,16 @@ struct vgasr_priv { | |||
| 51 | struct dentry *switch_file; | 51 | struct dentry *switch_file; |
| 52 | 52 | ||
| 53 | int registered_clients; | 53 | int registered_clients; |
| 54 | struct list_head clients; | 54 | struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS]; |
| 55 | 55 | ||
| 56 | struct vga_switcheroo_handler *handler; | 56 | struct vga_switcheroo_handler *handler; |
| 57 | }; | 57 | }; |
| 58 | 58 | ||
| 59 | #define ID_BIT_AUDIO 0x100 | ||
| 60 | #define client_is_audio(c) ((c)->id & ID_BIT_AUDIO) | ||
| 61 | #define client_is_vga(c) ((c)->id == -1 || !client_is_audio(c)) | ||
| 62 | #define client_id(c) ((c)->id & ~ID_BIT_AUDIO) | ||
| 63 | |||
| 64 | static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); | 59 | static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); |
| 65 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); | 60 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); |
| 66 | 61 | ||
| 67 | /* only one switcheroo per system */ | 62 | /* only one switcheroo per system */ |
| 68 | static struct vgasr_priv vgasr_priv = { | 63 | static struct vgasr_priv vgasr_priv; |
| 69 | .clients = LIST_HEAD_INIT(vgasr_priv.clients), | ||
| 70 | }; | ||
| 71 | |||
| 72 | static bool vga_switcheroo_ready(void) | ||
| 73 | { | ||
| 74 | /* we're ready if we get two clients + handler */ | ||
| 75 | return !vgasr_priv.active && | ||
| 76 | vgasr_priv.registered_clients == 2 && vgasr_priv.handler; | ||
| 77 | } | ||
| 78 | |||
| 79 | static void vga_switcheroo_enable(void) | ||
| 80 | { | ||
| 81 | int ret; | ||
| 82 | struct vga_switcheroo_client *client; | ||
| 83 | |||
| 84 | /* call the handler to init */ | ||
| 85 | if (vgasr_priv.handler->init) | ||
| 86 | vgasr_priv.handler->init(); | ||
| 87 | |||
| 88 | list_for_each_entry(client, &vgasr_priv.clients, list) { | ||
| 89 | if (client->id != -1) | ||
| 90 | continue; | ||
| 91 | ret = vgasr_priv.handler->get_client_id(client->pdev); | ||
| 92 | if (ret < 0) | ||
| 93 | return; | ||
| 94 | |||
| 95 | client->id = ret; | ||
| 96 | } | ||
| 97 | vga_switcheroo_debugfs_init(&vgasr_priv); | ||
| 98 | vgasr_priv.active = true; | ||
| 99 | } | ||
| 100 | 64 | ||
| 101 | int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) | 65 | int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) |
| 102 | { | 66 | { |
| @@ -107,10 +71,6 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) | |||
| 107 | } | 71 | } |
| 108 | 72 | ||
| 109 | vgasr_priv.handler = handler; | 73 | vgasr_priv.handler = handler; |
| 110 | if (vga_switcheroo_ready()) { | ||
| 111 | printk(KERN_INFO "vga_switcheroo: enabled\n"); | ||
| 112 | vga_switcheroo_enable(); | ||
| 113 | } | ||
| 114 | mutex_unlock(&vgasr_mutex); | 74 | mutex_unlock(&vgasr_mutex); |
| 115 | return 0; | 75 | return 0; |
| 116 | } | 76 | } |
| @@ -120,120 +80,78 @@ void vga_switcheroo_unregister_handler(void) | |||
| 120 | { | 80 | { |
| 121 | mutex_lock(&vgasr_mutex); | 81 | mutex_lock(&vgasr_mutex); |
| 122 | vgasr_priv.handler = NULL; | 82 | vgasr_priv.handler = NULL; |
| 123 | if (vgasr_priv.active) { | ||
| 124 | pr_info("vga_switcheroo: disabled\n"); | ||
| 125 | vga_switcheroo_debugfs_fini(&vgasr_priv); | ||
| 126 | vgasr_priv.active = false; | ||
| 127 | } | ||
| 128 | mutex_unlock(&vgasr_mutex); | 83 | mutex_unlock(&vgasr_mutex); |
| 129 | } | 84 | } |
| 130 | EXPORT_SYMBOL(vga_switcheroo_unregister_handler); | 85 | EXPORT_SYMBOL(vga_switcheroo_unregister_handler); |
| 131 | 86 | ||
| 132 | static int register_client(struct pci_dev *pdev, | 87 | static void vga_switcheroo_enable(void) |
| 133 | const struct vga_switcheroo_client_ops *ops, | ||
| 134 | int id, bool active) | ||
| 135 | { | 88 | { |
| 136 | struct vga_switcheroo_client *client; | 89 | int i; |
| 90 | int ret; | ||
| 91 | /* call the handler to init */ | ||
| 92 | vgasr_priv.handler->init(); | ||
| 137 | 93 | ||
| 138 | client = kzalloc(sizeof(*client), GFP_KERNEL); | 94 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 139 | if (!client) | 95 | ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev); |
| 140 | return -ENOMEM; | 96 | if (ret < 0) |
| 97 | return; | ||
| 141 | 98 | ||
| 142 | client->pwr_state = VGA_SWITCHEROO_ON; | 99 | vgasr_priv.clients[i].id = ret; |
| 143 | client->pdev = pdev; | 100 | } |
| 144 | client->ops = ops; | 101 | vga_switcheroo_debugfs_init(&vgasr_priv); |
| 145 | client->id = id; | 102 | vgasr_priv.active = true; |
| 146 | client->active = active; | 103 | } |
| 147 | 104 | ||
| 148 | mutex_lock(&vgasr_mutex); | 105 | int vga_switcheroo_register_client(struct pci_dev *pdev, |
| 149 | list_add_tail(&client->list, &vgasr_priv.clients); | 106 | void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state), |
| 150 | if (client_is_vga(client)) | 107 | void (*reprobe)(struct pci_dev *pdev), |
| 151 | vgasr_priv.registered_clients++; | 108 | bool (*can_switch)(struct pci_dev *pdev)) |
| 109 | { | ||
| 110 | int index; | ||
| 152 | 111 | ||
| 153 | if (vga_switcheroo_ready()) { | 112 | mutex_lock(&vgasr_mutex); |
| 113 | /* don't do IGD vs DIS here */ | ||
| 114 | if (vgasr_priv.registered_clients & 1) | ||
| 115 | index = 1; | ||
| 116 | else | ||
| 117 | index = 0; | ||
| 118 | |||
| 119 | vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON; | ||
| 120 | vgasr_priv.clients[index].pdev = pdev; | ||
| 121 | vgasr_priv.clients[index].set_gpu_state = set_gpu_state; | ||
| 122 | vgasr_priv.clients[index].reprobe = reprobe; | ||
| 123 | vgasr_priv.clients[index].can_switch = can_switch; | ||
| 124 | vgasr_priv.clients[index].id = -1; | ||
| 125 | if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) | ||
| 126 | vgasr_priv.clients[index].active = true; | ||
| 127 | |||
| 128 | vgasr_priv.registered_clients |= (1 << index); | ||
| 129 | |||
| 130 | /* if we get two clients + handler */ | ||
| 131 | if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) { | ||
| 154 | printk(KERN_INFO "vga_switcheroo: enabled\n"); | 132 | printk(KERN_INFO "vga_switcheroo: enabled\n"); |
| 155 | vga_switcheroo_enable(); | 133 | vga_switcheroo_enable(); |
| 156 | } | 134 | } |
| 157 | mutex_unlock(&vgasr_mutex); | 135 | mutex_unlock(&vgasr_mutex); |
| 158 | return 0; | 136 | return 0; |
| 159 | } | 137 | } |
| 160 | |||
| 161 | int vga_switcheroo_register_client(struct pci_dev *pdev, | ||
| 162 | const struct vga_switcheroo_client_ops *ops) | ||
| 163 | { | ||
| 164 | return register_client(pdev, ops, -1, | ||
| 165 | pdev == vga_default_device()); | ||
| 166 | } | ||
| 167 | EXPORT_SYMBOL(vga_switcheroo_register_client); | 138 | EXPORT_SYMBOL(vga_switcheroo_register_client); |
| 168 | 139 | ||
| 169 | int vga_switcheroo_register_audio_client(struct pci_dev *pdev, | ||
| 170 | const struct vga_switcheroo_client_ops *ops, | ||
| 171 | int id, bool active) | ||
| 172 | { | ||
| 173 | return register_client(pdev, ops, id | ID_BIT_AUDIO, active); | ||
| 174 | } | ||
| 175 | EXPORT_SYMBOL(vga_switcheroo_register_audio_client); | ||
| 176 | |||
| 177 | static struct vga_switcheroo_client * | ||
| 178 | find_client_from_pci(struct list_head *head, struct pci_dev *pdev) | ||
| 179 | { | ||
| 180 | struct vga_switcheroo_client *client; | ||
| 181 | list_for_each_entry(client, head, list) | ||
| 182 | if (client->pdev == pdev) | ||
| 183 | return client; | ||
| 184 | return NULL; | ||
| 185 | } | ||
| 186 | |||
| 187 | static struct vga_switcheroo_client * | ||
| 188 | find_client_from_id(struct list_head *head, int client_id) | ||
| 189 | { | ||
| 190 | struct vga_switcheroo_client *client; | ||
| 191 | list_for_each_entry(client, head, list) | ||
| 192 | if (client->id == client_id) | ||
| 193 | return client; | ||
| 194 | return NULL; | ||
| 195 | } | ||
| 196 | |||
| 197 | static struct vga_switcheroo_client * | ||
| 198 | find_active_client(struct list_head *head) | ||
| 199 | { | ||
| 200 | struct vga_switcheroo_client *client; | ||
| 201 | list_for_each_entry(client, head, list) | ||
| 202 | if (client->active && client_is_vga(client)) | ||
| 203 | return client; | ||
| 204 | return NULL; | ||
| 205 | } | ||
| 206 | |||
| 207 | int vga_switcheroo_get_client_state(struct pci_dev *pdev) | ||
| 208 | { | ||
| 209 | struct vga_switcheroo_client *client; | ||
| 210 | |||
| 211 | client = find_client_from_pci(&vgasr_priv.clients, pdev); | ||
| 212 | if (!client) | ||
| 213 | return VGA_SWITCHEROO_NOT_FOUND; | ||
| 214 | if (!vgasr_priv.active) | ||
| 215 | return VGA_SWITCHEROO_INIT; | ||
| 216 | return client->pwr_state; | ||
| 217 | } | ||
| 218 | EXPORT_SYMBOL(vga_switcheroo_get_client_state); | ||
| 219 | |||
| 220 | void vga_switcheroo_unregister_client(struct pci_dev *pdev) | 140 | void vga_switcheroo_unregister_client(struct pci_dev *pdev) |
| 221 | { | 141 | { |
| 222 | struct vga_switcheroo_client *client; | 142 | int i; |
| 223 | 143 | ||
| 224 | mutex_lock(&vgasr_mutex); | 144 | mutex_lock(&vgasr_mutex); |
| 225 | client = find_client_from_pci(&vgasr_priv.clients, pdev); | 145 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 226 | if (client) { | 146 | if (vgasr_priv.clients[i].pdev == pdev) { |
| 227 | if (client_is_vga(client)) | 147 | vgasr_priv.registered_clients &= ~(1 << i); |
| 228 | vgasr_priv.registered_clients--; | 148 | break; |
| 229 | list_del(&client->list); | 149 | } |
| 230 | kfree(client); | ||
| 231 | } | ||
| 232 | if (vgasr_priv.active && vgasr_priv.registered_clients < 2) { | ||
| 233 | printk(KERN_INFO "vga_switcheroo: disabled\n"); | ||
| 234 | vga_switcheroo_debugfs_fini(&vgasr_priv); | ||
| 235 | vgasr_priv.active = false; | ||
| 236 | } | 150 | } |
| 151 | |||
| 152 | printk(KERN_INFO "vga_switcheroo: disabled\n"); | ||
| 153 | vga_switcheroo_debugfs_fini(&vgasr_priv); | ||
| 154 | vgasr_priv.active = false; | ||
| 237 | mutex_unlock(&vgasr_mutex); | 155 | mutex_unlock(&vgasr_mutex); |
| 238 | } | 156 | } |
| 239 | EXPORT_SYMBOL(vga_switcheroo_unregister_client); | 157 | EXPORT_SYMBOL(vga_switcheroo_unregister_client); |
| @@ -241,29 +159,29 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_client); | |||
| 241 | void vga_switcheroo_client_fb_set(struct pci_dev *pdev, | 159 | void vga_switcheroo_client_fb_set(struct pci_dev *pdev, |
| 242 | struct fb_info *info) | 160 | struct fb_info *info) |
| 243 | { | 161 | { |
| 244 | struct vga_switcheroo_client *client; | 162 | int i; |
| 245 | 163 | ||
| 246 | mutex_lock(&vgasr_mutex); | 164 | mutex_lock(&vgasr_mutex); |
| 247 | client = find_client_from_pci(&vgasr_priv.clients, pdev); | 165 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 248 | if (client) | 166 | if (vgasr_priv.clients[i].pdev == pdev) { |
| 249 | client->fb_info = info; | 167 | vgasr_priv.clients[i].fb_info = info; |
| 168 | break; | ||
| 169 | } | ||
| 170 | } | ||
| 250 | mutex_unlock(&vgasr_mutex); | 171 | mutex_unlock(&vgasr_mutex); |
| 251 | } | 172 | } |
| 252 | EXPORT_SYMBOL(vga_switcheroo_client_fb_set); | 173 | EXPORT_SYMBOL(vga_switcheroo_client_fb_set); |
| 253 | 174 | ||
| 254 | static int vga_switcheroo_show(struct seq_file *m, void *v) | 175 | static int vga_switcheroo_show(struct seq_file *m, void *v) |
| 255 | { | 176 | { |
| 256 | struct vga_switcheroo_client *client; | 177 | int i; |
| 257 | int i = 0; | ||
| 258 | mutex_lock(&vgasr_mutex); | 178 | mutex_lock(&vgasr_mutex); |
| 259 | list_for_each_entry(client, &vgasr_priv.clients, list) { | 179 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 260 | seq_printf(m, "%d:%s%s:%c:%s:%s\n", i, | 180 | seq_printf(m, "%d:%s:%c:%s:%s\n", i, |
| 261 | client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", | 181 | vgasr_priv.clients[i].id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", |
| 262 | client_is_vga(client) ? "" : "-Audio", | 182 | vgasr_priv.clients[i].active ? '+' : ' ', |
| 263 | client->active ? '+' : ' ', | 183 | vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off", |
| 264 | client->pwr_state ? "Pwr" : "Off", | 184 | pci_name(vgasr_priv.clients[i].pdev)); |
| 265 | pci_name(client->pdev)); | ||
| 266 | i++; | ||
| 267 | } | 185 | } |
| 268 | mutex_unlock(&vgasr_mutex); | 186 | mutex_unlock(&vgasr_mutex); |
| 269 | return 0; | 187 | return 0; |
| @@ -279,7 +197,7 @@ static int vga_switchon(struct vga_switcheroo_client *client) | |||
| 279 | if (vgasr_priv.handler->power_state) | 197 | if (vgasr_priv.handler->power_state) |
| 280 | vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); | 198 | vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); |
| 281 | /* call the driver callback to turn on device */ | 199 | /* call the driver callback to turn on device */ |
| 282 | client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); | 200 | client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); |
| 283 | client->pwr_state = VGA_SWITCHEROO_ON; | 201 | client->pwr_state = VGA_SWITCHEROO_ON; |
| 284 | return 0; | 202 | return 0; |
| 285 | } | 203 | } |
| @@ -287,37 +205,34 @@ static int vga_switchon(struct vga_switcheroo_client *client) | |||
| 287 | static int vga_switchoff(struct vga_switcheroo_client *client) | 205 | static int vga_switchoff(struct vga_switcheroo_client *client) |
| 288 | { | 206 | { |
| 289 | /* call the driver callback to turn off device */ | 207 | /* call the driver callback to turn off device */ |
| 290 | client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); | 208 | client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); |
| 291 | if (vgasr_priv.handler->power_state) | 209 | if (vgasr_priv.handler->power_state) |
| 292 | vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); | 210 | vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); |
| 293 | client->pwr_state = VGA_SWITCHEROO_OFF; | 211 | client->pwr_state = VGA_SWITCHEROO_OFF; |
| 294 | return 0; | 212 | return 0; |
| 295 | } | 213 | } |
| 296 | 214 | ||
| 297 | static void set_audio_state(int id, int state) | ||
| 298 | { | ||
| 299 | struct vga_switcheroo_client *client; | ||
| 300 | |||
| 301 | client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO); | ||
| 302 | if (client && client->pwr_state != state) { | ||
| 303 | client->ops->set_gpu_state(client->pdev, state); | ||
| 304 | client->pwr_state = state; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | /* stage one happens before delay */ | 215 | /* stage one happens before delay */ |
| 309 | static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) | 216 | static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) |
| 310 | { | 217 | { |
| 311 | struct vga_switcheroo_client *active; | 218 | int i; |
| 219 | struct vga_switcheroo_client *active = NULL; | ||
| 312 | 220 | ||
| 313 | active = find_active_client(&vgasr_priv.clients); | 221 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 222 | if (vgasr_priv.clients[i].active == true) { | ||
| 223 | active = &vgasr_priv.clients[i]; | ||
| 224 | break; | ||
| 225 | } | ||
| 226 | } | ||
| 314 | if (!active) | 227 | if (!active) |
| 315 | return 0; | 228 | return 0; |
| 316 | 229 | ||
| 317 | if (new_client->pwr_state == VGA_SWITCHEROO_OFF) | 230 | if (new_client->pwr_state == VGA_SWITCHEROO_OFF) |
| 318 | vga_switchon(new_client); | 231 | vga_switchon(new_client); |
| 319 | 232 | ||
| 320 | vga_set_default_device(new_client->pdev); | 233 | /* swap shadow resource to denote boot VGA device has changed so X starts on new device */ |
| 234 | active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW; | ||
| 235 | new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; | ||
| 321 | return 0; | 236 | return 0; |
| 322 | } | 237 | } |
| 323 | 238 | ||
| @@ -325,16 +240,20 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) | |||
| 325 | static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) | 240 | static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) |
| 326 | { | 241 | { |
| 327 | int ret; | 242 | int ret; |
| 328 | struct vga_switcheroo_client *active; | 243 | int i; |
| 244 | struct vga_switcheroo_client *active = NULL; | ||
| 329 | 245 | ||
| 330 | active = find_active_client(&vgasr_priv.clients); | 246 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 247 | if (vgasr_priv.clients[i].active == true) { | ||
| 248 | active = &vgasr_priv.clients[i]; | ||
| 249 | break; | ||
| 250 | } | ||
| 251 | } | ||
| 331 | if (!active) | 252 | if (!active) |
| 332 | return 0; | 253 | return 0; |
| 333 | 254 | ||
| 334 | active->active = false; | 255 | active->active = false; |
| 335 | 256 | ||
| 336 | set_audio_state(active->id, VGA_SWITCHEROO_OFF); | ||
| 337 | |||
| 338 | if (new_client->fb_info) { | 257 | if (new_client->fb_info) { |
| 339 | struct fb_event event; | 258 | struct fb_event event; |
| 340 | event.info = new_client->fb_info; | 259 | event.info = new_client->fb_info; |
| @@ -345,37 +264,23 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) | |||
| 345 | if (ret) | 264 | if (ret) |
| 346 | return ret; | 265 | return ret; |
| 347 | 266 | ||
| 348 | if (new_client->ops->reprobe) | 267 | if (new_client->reprobe) |
| 349 | new_client->ops->reprobe(new_client->pdev); | 268 | new_client->reprobe(new_client->pdev); |
| 350 | 269 | ||
| 351 | if (active->pwr_state == VGA_SWITCHEROO_ON) | 270 | if (active->pwr_state == VGA_SWITCHEROO_ON) |
| 352 | vga_switchoff(active); | 271 | vga_switchoff(active); |
| 353 | 272 | ||
| 354 | set_audio_state(new_client->id, VGA_SWITCHEROO_ON); | ||
| 355 | |||
| 356 | new_client->active = true; | 273 | new_client->active = true; |
| 357 | return 0; | 274 | return 0; |
| 358 | } | 275 | } |
| 359 | 276 | ||
| 360 | static bool check_can_switch(void) | ||
| 361 | { | ||
| 362 | struct vga_switcheroo_client *client; | ||
| 363 | |||
| 364 | list_for_each_entry(client, &vgasr_priv.clients, list) { | ||
| 365 | if (!client->ops->can_switch(client->pdev)) { | ||
| 366 | printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id); | ||
| 367 | return false; | ||
| 368 | } | ||
| 369 | } | ||
| 370 | return true; | ||
| 371 | } | ||
| 372 | |||
| 373 | static ssize_t | 277 | static ssize_t |
| 374 | vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | 278 | vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, |
| 375 | size_t cnt, loff_t *ppos) | 279 | size_t cnt, loff_t *ppos) |
| 376 | { | 280 | { |
| 377 | char usercmd[64]; | 281 | char usercmd[64]; |
| 378 | int ret; | 282 | const char *pdev_name; |
| 283 | int i, ret; | ||
| 379 | bool delay = false, can_switch; | 284 | bool delay = false, can_switch; |
| 380 | bool just_mux = false; | 285 | bool just_mux = false; |
| 381 | int client_id = -1; | 286 | int client_id = -1; |
| @@ -396,23 +301,21 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | |||
| 396 | 301 | ||
| 397 | /* pwr off the device not in use */ | 302 | /* pwr off the device not in use */ |
| 398 | if (strncmp(usercmd, "OFF", 3) == 0) { | 303 | if (strncmp(usercmd, "OFF", 3) == 0) { |
| 399 | list_for_each_entry(client, &vgasr_priv.clients, list) { | 304 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 400 | if (client->active || client_is_audio(client)) | 305 | if (vgasr_priv.clients[i].active) |
| 401 | continue; | 306 | continue; |
| 402 | set_audio_state(client->id, VGA_SWITCHEROO_OFF); | 307 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON) |
| 403 | if (client->pwr_state == VGA_SWITCHEROO_ON) | 308 | vga_switchoff(&vgasr_priv.clients[i]); |
| 404 | vga_switchoff(client); | ||
| 405 | } | 309 | } |
| 406 | goto out; | 310 | goto out; |
| 407 | } | 311 | } |
| 408 | /* pwr on the device not in use */ | 312 | /* pwr on the device not in use */ |
| 409 | if (strncmp(usercmd, "ON", 2) == 0) { | 313 | if (strncmp(usercmd, "ON", 2) == 0) { |
| 410 | list_for_each_entry(client, &vgasr_priv.clients, list) { | 314 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 411 | if (client->active || client_is_audio(client)) | 315 | if (vgasr_priv.clients[i].active) |
| 412 | continue; | 316 | continue; |
| 413 | if (client->pwr_state == VGA_SWITCHEROO_OFF) | 317 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF) |
| 414 | vga_switchon(client); | 318 | vga_switchon(&vgasr_priv.clients[i]); |
| 415 | set_audio_state(client->id, VGA_SWITCHEROO_ON); | ||
| 416 | } | 319 | } |
| 417 | goto out; | 320 | goto out; |
| 418 | } | 321 | } |
| @@ -445,9 +348,13 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | |||
| 445 | 348 | ||
| 446 | if (client_id == -1) | 349 | if (client_id == -1) |
| 447 | goto out; | 350 | goto out; |
| 448 | client = find_client_from_id(&vgasr_priv.clients, client_id); | 351 | |
| 449 | if (!client) | 352 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 450 | goto out; | 353 | if (vgasr_priv.clients[i].id == client_id) { |
| 354 | client = &vgasr_priv.clients[i]; | ||
| 355 | break; | ||
| 356 | } | ||
| 357 | } | ||
| 451 | 358 | ||
| 452 | vgasr_priv.delayed_switch_active = false; | 359 | vgasr_priv.delayed_switch_active = false; |
| 453 | 360 | ||
| @@ -456,16 +363,24 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | |||
| 456 | goto out; | 363 | goto out; |
| 457 | } | 364 | } |
| 458 | 365 | ||
| 459 | if (client->active) | 366 | if (client->active == true) |
| 460 | goto out; | 367 | goto out; |
| 461 | 368 | ||
| 462 | /* okay we want a switch - test if devices are willing to switch */ | 369 | /* okay we want a switch - test if devices are willing to switch */ |
| 463 | can_switch = check_can_switch(); | 370 | can_switch = true; |
| 371 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
| 372 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
| 373 | if (can_switch == false) { | ||
| 374 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
| 375 | break; | ||
| 376 | } | ||
| 377 | } | ||
| 464 | 378 | ||
| 465 | if (can_switch == false && delay == false) | 379 | if (can_switch == false && delay == false) |
| 466 | goto out; | 380 | goto out; |
| 467 | 381 | ||
| 468 | if (can_switch) { | 382 | if (can_switch == true) { |
| 383 | pdev_name = pci_name(client->pdev); | ||
| 469 | ret = vga_switchto_stage1(client); | 384 | ret = vga_switchto_stage1(client); |
| 470 | if (ret) | 385 | if (ret) |
| 471 | printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret); | 386 | printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret); |
| @@ -536,7 +451,10 @@ fail: | |||
| 536 | 451 | ||
| 537 | int vga_switcheroo_process_delayed_switch(void) | 452 | int vga_switcheroo_process_delayed_switch(void) |
| 538 | { | 453 | { |
| 539 | struct vga_switcheroo_client *client; | 454 | struct vga_switcheroo_client *client = NULL; |
| 455 | const char *pdev_name; | ||
| 456 | bool can_switch = true; | ||
| 457 | int i; | ||
| 540 | int ret; | 458 | int ret; |
| 541 | int err = -EINVAL; | 459 | int err = -EINVAL; |
| 542 | 460 | ||
| @@ -546,11 +464,20 @@ int vga_switcheroo_process_delayed_switch(void) | |||
| 546 | 464 | ||
| 547 | printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); | 465 | printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); |
| 548 | 466 | ||
| 549 | client = find_client_from_id(&vgasr_priv.clients, | 467 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { |
| 550 | vgasr_priv.delayed_client_id); | 468 | if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id) |
| 551 | if (!client || !check_can_switch()) | 469 | client = &vgasr_priv.clients[i]; |
| 470 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
| 471 | if (can_switch == false) { | ||
| 472 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
| 473 | break; | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | if (can_switch == false || client == NULL) | ||
| 552 | goto err; | 478 | goto err; |
| 553 | 479 | ||
| 480 | pdev_name = pci_name(client->pdev); | ||
| 554 | ret = vga_switchto_stage2(client); | 481 | ret = vga_switchto_stage2(client); |
| 555 | if (ret) | 482 | if (ret) |
| 556 | printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret); | 483 | printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret); |
| @@ -562,3 +489,4 @@ err: | |||
| 562 | return err; | 489 | return err; |
| 563 | } | 490 | } |
| 564 | EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); | 491 | EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); |
| 492 | |||
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index e893f6e1937..c72f1c0b5e6 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c | |||
| @@ -136,17 +136,6 @@ struct pci_dev *vga_default_device(void) | |||
| 136 | { | 136 | { |
| 137 | return vga_default; | 137 | return vga_default; |
| 138 | } | 138 | } |
| 139 | |||
| 140 | EXPORT_SYMBOL_GPL(vga_default_device); | ||
| 141 | |||
| 142 | void vga_set_default_device(struct pci_dev *pdev) | ||
| 143 | { | ||
| 144 | if (vga_default == pdev) | ||
| 145 | return; | ||
| 146 | |||
| 147 | pci_dev_put(vga_default); | ||
| 148 | vga_default = pci_dev_get(pdev); | ||
| 149 | } | ||
| 150 | #endif | 139 | #endif |
| 151 | 140 | ||
| 152 | static inline void vga_irq_set_state(struct vga_device *vgadev, bool state) | 141 | static inline void vga_irq_set_state(struct vga_device *vgadev, bool state) |
| @@ -476,29 +465,31 @@ static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev) | |||
| 476 | while (new_bus) { | 465 | while (new_bus) { |
| 477 | new_bridge = new_bus->self; | 466 | new_bridge = new_bus->self; |
| 478 | 467 | ||
| 479 | /* go through list of devices already registered */ | 468 | if (new_bridge) { |
| 480 | list_for_each_entry(same_bridge_vgadev, &vga_list, list) { | 469 | /* go through list of devices already registered */ |
| 481 | bus = same_bridge_vgadev->pdev->bus; | 470 | list_for_each_entry(same_bridge_vgadev, &vga_list, list) { |
| 482 | bridge = bus->self; | 471 | bus = same_bridge_vgadev->pdev->bus; |
| 483 | |||
| 484 | /* see if the share a bridge with this device */ | ||
| 485 | if (new_bridge == bridge) { | ||
| 486 | /* if their direct parent bridge is the same | ||
| 487 | as any bridge of this device then it can't be used | ||
| 488 | for that device */ | ||
| 489 | same_bridge_vgadev->bridge_has_one_vga = false; | ||
| 490 | } | ||
| 491 | |||
| 492 | /* now iterate the previous devices bridge hierarchy */ | ||
| 493 | /* if the new devices parent bridge is in the other devices | ||
| 494 | hierarchy then we can't use it to control this device */ | ||
| 495 | while (bus) { | ||
| 496 | bridge = bus->self; | 472 | bridge = bus->self; |
| 497 | if (bridge) { | 473 | |
| 498 | if (bridge == vgadev->pdev->bus->self) | 474 | /* see if the share a bridge with this device */ |
| 499 | vgadev->bridge_has_one_vga = false; | 475 | if (new_bridge == bridge) { |
| 476 | /* if their direct parent bridge is the same | ||
| 477 | as any bridge of this device then it can't be used | ||
| 478 | for that device */ | ||
| 479 | same_bridge_vgadev->bridge_has_one_vga = false; | ||
| 480 | } | ||
| 481 | |||
| 482 | /* now iterate the previous devices bridge hierarchy */ | ||
| 483 | /* if the new devices parent bridge is in the other devices | ||
| 484 | hierarchy then we can't use it to control this device */ | ||
| 485 | while (bus) { | ||
| 486 | bridge = bus->self; | ||
| 487 | if (bridge) { | ||
| 488 | if (bridge == vgadev->pdev->bus->self) | ||
| 489 | vgadev->bridge_has_one_vga = false; | ||
| 490 | } | ||
| 491 | bus = bus->parent; | ||
| 500 | } | 492 | } |
| 501 | bus = bus->parent; | ||
| 502 | } | 493 | } |
| 503 | } | 494 | } |
| 504 | new_bus = new_bus->parent; | 495 | new_bus = new_bus->parent; |
| @@ -581,7 +572,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) | |||
| 581 | #ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE | 572 | #ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE |
| 582 | if (vga_default == NULL && | 573 | if (vga_default == NULL && |
| 583 | ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) | 574 | ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) |
| 584 | vga_set_default_device(pdev); | 575 | vga_default = pci_dev_get(pdev); |
| 585 | #endif | 576 | #endif |
| 586 | 577 | ||
| 587 | vga_arbiter_check_bridge_sharing(vgadev); | 578 | vga_arbiter_check_bridge_sharing(vgadev); |
| @@ -616,10 +607,10 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev) | |||
| 616 | goto bail; | 607 | goto bail; |
| 617 | } | 608 | } |
| 618 | 609 | ||
| 619 | #ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE | 610 | if (vga_default == pdev) { |
| 620 | if (vga_default == pdev) | 611 | pci_dev_put(vga_default); |
| 621 | vga_set_default_device(NULL); | 612 | vga_default = NULL; |
| 622 | #endif | 613 | } |
| 623 | 614 | ||
| 624 | if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM)) | 615 | if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM)) |
| 625 | vga_decode_count--; | 616 | vga_decode_count--; |
| @@ -1002,20 +993,14 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf, | |||
| 1002 | uc = &priv->cards[i]; | 993 | uc = &priv->cards[i]; |
| 1003 | } | 994 | } |
| 1004 | 995 | ||
| 1005 | if (!uc) { | 996 | if (!uc) |
| 1006 | ret_val = -EINVAL; | 997 | return -EINVAL; |
| 1007 | goto done; | ||
| 1008 | } | ||
| 1009 | 998 | ||
| 1010 | if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0) { | 999 | if (io_state & VGA_RSRC_LEGACY_IO && uc->io_cnt == 0) |
| 1011 | ret_val = -EINVAL; | 1000 | return -EINVAL; |
| 1012 | goto done; | ||
| 1013 | } | ||
| 1014 | 1001 | ||
| 1015 | if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0) { | 1002 | if (io_state & VGA_RSRC_LEGACY_MEM && uc->mem_cnt == 0) |
| 1016 | ret_val = -EINVAL; | 1003 | return -EINVAL; |
| 1017 | goto done; | ||
| 1018 | } | ||
| 1019 | 1004 | ||
| 1020 | vga_put(pdev, io_state); | 1005 | vga_put(pdev, io_state); |
| 1021 | 1006 | ||
| @@ -1068,6 +1053,7 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf, | |||
| 1068 | } | 1053 | } |
| 1069 | 1054 | ||
| 1070 | } else if (strncmp(curr_pos, "target ", 7) == 0) { | 1055 | } else if (strncmp(curr_pos, "target ", 7) == 0) { |
| 1056 | struct pci_bus *pbus; | ||
| 1071 | unsigned int domain, bus, devfn; | 1057 | unsigned int domain, bus, devfn; |
| 1072 | struct vga_device *vgadev; | 1058 | struct vga_device *vgadev; |
| 1073 | 1059 | ||
| @@ -1086,11 +1072,19 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf, | |||
| 1086 | pr_debug("vgaarb: %s ==> %x:%x:%x.%x\n", curr_pos, | 1072 | pr_debug("vgaarb: %s ==> %x:%x:%x.%x\n", curr_pos, |
| 1087 | domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); | 1073 | domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
| 1088 | 1074 | ||
| 1089 | pdev = pci_get_domain_bus_and_slot(domain, bus, devfn); | 1075 | pbus = pci_find_bus(domain, bus); |
| 1076 | pr_debug("vgaarb: pbus %p\n", pbus); | ||
| 1077 | if (pbus == NULL) { | ||
| 1078 | pr_err("vgaarb: invalid PCI domain and/or bus address %x:%x\n", | ||
| 1079 | domain, bus); | ||
| 1080 | ret_val = -ENODEV; | ||
| 1081 | goto done; | ||
| 1082 | } | ||
| 1083 | pdev = pci_get_slot(pbus, devfn); | ||
| 1090 | pr_debug("vgaarb: pdev %p\n", pdev); | 1084 | pr_debug("vgaarb: pdev %p\n", pdev); |
| 1091 | if (!pdev) { | 1085 | if (!pdev) { |
| 1092 | pr_err("vgaarb: invalid PCI address %x:%x:%x\n", | 1086 | pr_err("vgaarb: invalid PCI address %x:%x\n", |
| 1093 | domain, bus, devfn); | 1087 | bus, devfn); |
| 1094 | ret_val = -ENODEV; | 1088 | ret_val = -ENODEV; |
| 1095 | goto done; | 1089 | goto done; |
| 1096 | } | 1090 | } |
