vga_switcheroo.c 13.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * Copyright (c) 2010 Red Hat Inc.
 * Author : Dave Airlie <airlied@redhat.com>
 *
 *
 * Licensed under GPLv2
 *
 * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs

 Switcher interface - methods require for ATPX and DCM
 - switchto - this throws the output MUX switch
 - discrete_set_power - sets the power state for the discrete card

 GPU driver interface
 - set_gpu_state - this should do the equiv of s/r for the card
		  - this should *not* set the discrete power state
 - switch_check  - check if the device is in a position to switch now
 */

#include <linux/module.h>
#include <linux/dmi.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/fb.h>

#include <linux/pci.h>
#include <linux/vga_switcheroo.h>

31 32
#include <linux/vgaarb.h>

33 34 35 36
struct vga_switcheroo_client {
	struct pci_dev *pdev;
	struct fb_info *fb_info;
	int pwr_state;
37
	const struct vga_switcheroo_client_ops *ops;
38 39
	int id;
	bool active;
40
	struct list_head list;
41 42 43 44 45 46 47 48 49 50 51 52 53 54
};

static DEFINE_MUTEX(vgasr_mutex);

struct vgasr_priv {

	bool active;
	bool delayed_switch_active;
	enum vga_switcheroo_client_id delayed_client_id;

	struct dentry *debugfs_root;
	struct dentry *switch_file;

	int registered_clients;
55
	struct list_head clients;
56 57 58 59

	struct vga_switcheroo_handler *handler;
};

60 61 62 63 64
#define ID_BIT_AUDIO		0x100
#define client_is_audio(c)	((c)->id & ID_BIT_AUDIO)
#define client_is_vga(c)	((c)->id == -1 || !client_is_audio(c))
#define client_id(c)		((c)->id & ~ID_BIT_AUDIO)

65 66 67 68
static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv);
static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);

/* only one switcheroo per system */
69 70 71
static struct vgasr_priv vgasr_priv = {
	.clients = LIST_HEAD_INIT(vgasr_priv.clients),
};
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
{
	mutex_lock(&vgasr_mutex);
	if (vgasr_priv.handler) {
		mutex_unlock(&vgasr_mutex);
		return -EINVAL;
	}

	vgasr_priv.handler = handler;
	mutex_unlock(&vgasr_mutex);
	return 0;
}
EXPORT_SYMBOL(vga_switcheroo_register_handler);

void vga_switcheroo_unregister_handler(void)
{
	mutex_lock(&vgasr_mutex);
	vgasr_priv.handler = NULL;
	mutex_unlock(&vgasr_mutex);
}
EXPORT_SYMBOL(vga_switcheroo_unregister_handler);

static void vga_switcheroo_enable(void)
{
	int ret;
98 99
	struct vga_switcheroo_client *client;

100 101 102
	/* call the handler to init */
	vgasr_priv.handler->init();

103
	list_for_each_entry(client, &vgasr_priv.clients, list) {
104 105
		if (client->id != -1)
			continue;
106
		ret = vgasr_priv.handler->get_client_id(client->pdev);
107 108 109
		if (ret < 0)
			return;

110
		client->id = ret;
111 112 113 114 115
	}
	vga_switcheroo_debugfs_init(&vgasr_priv);
	vgasr_priv.active = true;
}

116 117 118
static int register_client(struct pci_dev *pdev,
			   const struct vga_switcheroo_client_ops *ops,
			   int id, bool active)
119
{
120
	struct vga_switcheroo_client *client;
121

122 123 124 125 126 127
	client = kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client)
		return -ENOMEM;

	client->pwr_state = VGA_SWITCHEROO_ON;
	client->pdev = pdev;
128
	client->ops = ops;
129 130
	client->id = id;
	client->active = active;
131

132 133
	mutex_lock(&vgasr_mutex);
	list_add_tail(&client->list, &vgasr_priv.clients);
134 135
	if (client_is_vga(client))
		vgasr_priv.registered_clients++;
136 137

	/* if we get two clients + handler */
138 139
	if (!vgasr_priv.active &&
	    vgasr_priv.registered_clients == 2 && vgasr_priv.handler) {
140 141 142 143 144 145
		printk(KERN_INFO "vga_switcheroo: enabled\n");
		vga_switcheroo_enable();
	}
	mutex_unlock(&vgasr_mutex);
	return 0;
}
146 147 148 149 150 151 152

int vga_switcheroo_register_client(struct pci_dev *pdev,
				   const struct vga_switcheroo_client_ops *ops)
{
	return register_client(pdev, ops, -1,
			       pdev == vga_default_device());
}
153 154
EXPORT_SYMBOL(vga_switcheroo_register_client);

155 156 157 158 159 160 161 162
int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
					 const struct vga_switcheroo_client_ops *ops,
					 int id, bool active)
{
	return register_client(pdev, ops, id | ID_BIT_AUDIO, active);
}
EXPORT_SYMBOL(vga_switcheroo_register_audio_client);

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
static struct vga_switcheroo_client *
find_client_from_pci(struct list_head *head, struct pci_dev *pdev)
{
	struct vga_switcheroo_client *client;
	list_for_each_entry(client, head, list)
		if (client->pdev == pdev)
			return client;
	return NULL;
}

static struct vga_switcheroo_client *
find_client_from_id(struct list_head *head, int client_id)
{
	struct vga_switcheroo_client *client;
	list_for_each_entry(client, head, list)
		if (client->id == client_id)
			return client;
	return NULL;
}

static struct vga_switcheroo_client *
find_active_client(struct list_head *head)
{
	struct vga_switcheroo_client *client;
	list_for_each_entry(client, head, list)
188
		if (client->active && client_is_vga(client))
189 190 191 192
			return client;
	return NULL;
}

193 194
void vga_switcheroo_unregister_client(struct pci_dev *pdev)
{
195
	struct vga_switcheroo_client *client;
196 197

	mutex_lock(&vgasr_mutex);
198 199
	client = find_client_from_pci(&vgasr_priv.clients, pdev);
	if (client) {
200 201
		if (client_is_vga(client))
			vgasr_priv.registered_clients--;
202 203
		list_del(&client->list);
		kfree(client);
204
	}
205 206 207 208 209
	if (vgasr_priv.active && vgasr_priv.registered_clients < 2) {
		printk(KERN_INFO "vga_switcheroo: disabled\n");
		vga_switcheroo_debugfs_fini(&vgasr_priv);
		vgasr_priv.active = false;
	}
210 211 212 213 214 215 216
	mutex_unlock(&vgasr_mutex);
}
EXPORT_SYMBOL(vga_switcheroo_unregister_client);

void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
				 struct fb_info *info)
{
217
	struct vga_switcheroo_client *client;
218 219

	mutex_lock(&vgasr_mutex);
220 221 222
	client = find_client_from_pci(&vgasr_priv.clients, pdev);
	if (client)
		client->fb_info = info;
223 224 225 226 227 228
	mutex_unlock(&vgasr_mutex);
}
EXPORT_SYMBOL(vga_switcheroo_client_fb_set);

static int vga_switcheroo_show(struct seq_file *m, void *v)
{
229 230
	struct vga_switcheroo_client *client;
	int i = 0;
231
	mutex_lock(&vgasr_mutex);
232
	list_for_each_entry(client, &vgasr_priv.clients, list) {
233 234 235
		seq_printf(m, "%d:%s%s:%c:%s:%s\n", i,
			   client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD",
			   client_is_vga(client) ? "" : "-Audio",
236 237 238 239
			   client->active ? '+' : ' ',
			   client->pwr_state ? "Pwr" : "Off",
			   pci_name(client->pdev));
		i++;
240 241 242 243 244 245 246 247 248 249 250 251
	}
	mutex_unlock(&vgasr_mutex);
	return 0;
}

static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
{
	return single_open(file, vga_switcheroo_show, NULL);
}

static int vga_switchon(struct vga_switcheroo_client *client)
{
252 253
	if (vgasr_priv.handler->power_state)
		vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);
254
	/* call the driver callback to turn on device */
255
	client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON);
256 257 258 259 260 261 262
	client->pwr_state = VGA_SWITCHEROO_ON;
	return 0;
}

static int vga_switchoff(struct vga_switcheroo_client *client)
{
	/* call the driver callback to turn off device */
263
	client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);
264 265
	if (vgasr_priv.handler->power_state)
		vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF);
266 267 268 269
	client->pwr_state = VGA_SWITCHEROO_OFF;
	return 0;
}

270 271 272 273 274 275 276 277 278 279 280
static void set_audio_state(int id, int state)
{
	struct vga_switcheroo_client *client;

	client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO);
	if (client && client->pwr_state != state) {
		client->ops->set_gpu_state(client->pdev, state);
		client->pwr_state = state;
	}
}

281 282
/* stage one happens before delay */
static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
283
{
284
	struct vga_switcheroo_client *active;
285

286
	active = find_active_client(&vgasr_priv.clients);
287 288 289 290 291 292
	if (!active)
		return 0;

	if (new_client->pwr_state == VGA_SWITCHEROO_OFF)
		vga_switchon(new_client);

293
	vga_set_default_device(new_client->pdev);
294
	set_audio_state(new_client->id, VGA_SWITCHEROO_ON);
295

296 297 298 299 300 301 302
	return 0;
}

/* post delay */
static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
{
	int ret;
303
	struct vga_switcheroo_client *active;
304

305
	active = find_active_client(&vgasr_priv.clients);
306 307 308 309
	if (!active)
		return 0;

	active->active = false;
310 311 312 313 314 315 316 317 318 319 320

	if (new_client->fb_info) {
		struct fb_event event;
		event.info = new_client->fb_info;
		fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);
	}

	ret = vgasr_priv.handler->switchto(new_client->id);
	if (ret)
		return ret;

321 322
	if (new_client->ops->reprobe)
		new_client->ops->reprobe(new_client->pdev);
323

324 325
	set_audio_state(active->id, VGA_SWITCHEROO_OFF);

326 327 328 329 330 331 332
	if (active->pwr_state == VGA_SWITCHEROO_ON)
		vga_switchoff(active);

	new_client->active = true;
	return 0;
}

333 334 335 336 337
static bool check_can_switch(void)
{
	struct vga_switcheroo_client *client;

	list_for_each_entry(client, &vgasr_priv.clients, list) {
338
		if (!client->ops->can_switch(client->pdev)) {
339 340 341 342 343 344 345
			printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id);
			return false;
		}
	}
	return true;
}

346 347 348 349 350 351
static ssize_t
vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
			     size_t cnt, loff_t *ppos)
{
	char usercmd[64];
	const char *pdev_name;
352
	int ret;
353
	bool delay = false, can_switch;
354
	bool just_mux = false;
355 356 357 358 359 360 361 362 363 364 365
	int client_id = -1;
	struct vga_switcheroo_client *client = NULL;

	if (cnt > 63)
		cnt = 63;

	if (copy_from_user(usercmd, ubuf, cnt))
		return -EFAULT;

	mutex_lock(&vgasr_mutex);

366 367 368 369
	if (!vgasr_priv.active) {
		cnt = -EINVAL;
		goto out;
	}
370 371 372

	/* pwr off the device not in use */
	if (strncmp(usercmd, "OFF", 3) == 0) {
373 374
		list_for_each_entry(client, &vgasr_priv.clients, list) {
			if (client->active)
375
				continue;
376 377
			if (client->pwr_state == VGA_SWITCHEROO_ON)
				vga_switchoff(client);
378 379 380 381 382
		}
		goto out;
	}
	/* pwr on the device not in use */
	if (strncmp(usercmd, "ON", 2) == 0) {
383 384
		list_for_each_entry(client, &vgasr_priv.clients, list) {
			if (client->active)
385
				continue;
386 387
			if (client->pwr_state == VGA_SWITCHEROO_OFF)
				vga_switchon(client);
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
		}
		goto out;
	}

	/* request a delayed switch - test can we switch now */
	if (strncmp(usercmd, "DIGD", 4) == 0) {
		client_id = VGA_SWITCHEROO_IGD;
		delay = true;
	}

	if (strncmp(usercmd, "DDIS", 4) == 0) {
		client_id = VGA_SWITCHEROO_DIS;
		delay = true;
	}

	if (strncmp(usercmd, "IGD", 3) == 0)
		client_id = VGA_SWITCHEROO_IGD;

	if (strncmp(usercmd, "DIS", 3) == 0)
		client_id = VGA_SWITCHEROO_DIS;

409
	if (strncmp(usercmd, "MIGD", 4) == 0) {
410 411 412
		just_mux = true;
		client_id = VGA_SWITCHEROO_IGD;
	}
413
	if (strncmp(usercmd, "MDIS", 4) == 0) {
414 415 416 417
		just_mux = true;
		client_id = VGA_SWITCHEROO_DIS;
	}

418 419
	if (client_id == -1)
		goto out;
420 421 422
	client = find_client_from_id(&vgasr_priv.clients, client_id);
	if (!client)
		goto out;
423 424

	vgasr_priv.delayed_switch_active = false;
425 426 427 428 429 430

	if (just_mux) {
		ret = vgasr_priv.handler->switchto(client_id);
		goto out;
	}

431
	if (client->active)
432 433
		goto out;

434
	/* okay we want a switch - test if devices are willing to switch */
435
	can_switch = check_can_switch();
436 437 438 439

	if (can_switch == false && delay == false)
		goto out;

440
	if (can_switch) {
441
		pdev_name = pci_name(client->pdev);
442 443 444 445 446
		ret = vga_switchto_stage1(client);
		if (ret)
			printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret);

		ret = vga_switchto_stage2(client);
447
		if (ret)
448 449
			printk(KERN_ERR "vga_switcheroo: switching failed stage 2 %d\n", ret);

450 451 452 453 454
	} else {
		printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id);
		vgasr_priv.delayed_switch_active = true;
		vgasr_priv.delayed_client_id = client_id;

455 456 457
		ret = vga_switchto_stage1(client);
		if (ret)
			printk(KERN_ERR "vga_switcheroo: delayed switching stage 1 failed %d\n", ret);
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
	}

out:
	mutex_unlock(&vgasr_mutex);
	return cnt;
}

static const struct file_operations vga_switcheroo_debugfs_fops = {
	.owner = THIS_MODULE,
	.open = vga_switcheroo_debugfs_open,
	.write = vga_switcheroo_debugfs_write,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
};

static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv)
{
	if (priv->switch_file) {
		debugfs_remove(priv->switch_file);
		priv->switch_file = NULL;
	}
	if (priv->debugfs_root) {
		debugfs_remove(priv->debugfs_root);
		priv->debugfs_root = NULL;
	}
}

static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv)
{
	/* already initialised */
	if (priv->debugfs_root)
		return 0;
	priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL);

	if (!priv->debugfs_root) {
		printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n");
		goto fail;
	}

	priv->switch_file = debugfs_create_file("switch", 0644,
						priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops);
	if (!priv->switch_file) {
		printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n");
		goto fail;
	}
	return 0;
fail:
	vga_switcheroo_debugfs_fini(priv);
	return -1;
}

int vga_switcheroo_process_delayed_switch(void)
{
512
	struct vga_switcheroo_client *client;
513 514 515 516 517 518 519 520 521 522
	const char *pdev_name;
	int ret;
	int err = -EINVAL;

	mutex_lock(&vgasr_mutex);
	if (!vgasr_priv.delayed_switch_active)
		goto err;

	printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id);

523 524 525
	client = find_client_from_id(&vgasr_priv.clients,
				     vgasr_priv.delayed_client_id);
	if (!client || !check_can_switch())
526 527 528
		goto err;

	pdev_name = pci_name(client->pdev);
529
	ret = vga_switchto_stage2(client);
530
	if (ret)
531
		printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret);
532 533 534 535 536 537 538 539 540

	vgasr_priv.delayed_switch_active = false;
	err = 0;
err:
	mutex_unlock(&vgasr_mutex);
	return err;
}
EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);