diff options
Diffstat (limited to 'drivers/gpu/vga/vga_switcheroo.c')
| -rw-r--r-- | drivers/gpu/vga/vga_switcheroo.c | 537 | 
1 files changed, 395 insertions, 142 deletions
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index c8768f38511..6866448083b 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -18,7 +18,6 @@   */  #include <linux/module.h> -#include <linux/dmi.h>  #include <linux/seq_file.h>  #include <linux/uaccess.h>  #include <linux/fs.h> @@ -26,16 +25,21 @@  #include <linux/fb.h>  #include <linux/pci.h> +#include <linux/console.h>  #include <linux/vga_switcheroo.h> +#include <linux/pm_runtime.h> + +#include <linux/vgaarb.h>  struct vga_switcheroo_client {  	struct pci_dev *pdev;  	struct fb_info *fb_info;  	int pwr_state; -	void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state); -	bool (*can_switch)(struct pci_dev *pdev); +	const struct vga_switcheroo_client_ops *ops;  	int id;  	bool active; +	bool driver_power_control; +	struct list_head list;  };  static DEFINE_MUTEX(vgasr_mutex); @@ -50,16 +54,52 @@ struct vgasr_priv {  	struct dentry *switch_file;  	int registered_clients; -	struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS]; +	struct list_head clients;  	struct vga_switcheroo_handler *handler;  }; +#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) +  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 */ -static struct vgasr_priv vgasr_priv; +static struct vgasr_priv vgasr_priv = { +	.clients = LIST_HEAD_INIT(vgasr_priv.clients), +}; + +static bool vga_switcheroo_ready(void) +{ +	/* we're ready if we get two clients + handler */ +	return !vgasr_priv.active && +	       vgasr_priv.registered_clients == 2 && vgasr_priv.handler; +} + +static void vga_switcheroo_enable(void) +{ +	int ret; +	struct vga_switcheroo_client *client; + +	/* call the handler to init */ +	if (vgasr_priv.handler->init) +		vgasr_priv.handler->init(); + +	list_for_each_entry(client, &vgasr_priv.clients, list) { +		if (client->id != -1) +			continue; +		ret = vgasr_priv.handler->get_client_id(client->pdev); +		if (ret < 0) +			return; + +		client->id = ret; +	} +	vga_switcheroo_debugfs_init(&vgasr_priv); +	vgasr_priv.active = true; +}  int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)  { @@ -70,6 +110,10 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)  	}  	vgasr_priv.handler = handler; +	if (vga_switcheroo_ready()) { +		printk(KERN_INFO "vga_switcheroo: enabled\n"); +		vga_switcheroo_enable(); +	}  	mutex_unlock(&vgasr_mutex);  	return 0;  } @@ -79,76 +123,122 @@ void vga_switcheroo_unregister_handler(void)  {  	mutex_lock(&vgasr_mutex);  	vgasr_priv.handler = NULL; +	if (vgasr_priv.active) { +		pr_info("vga_switcheroo: disabled\n"); +		vga_switcheroo_debugfs_fini(&vgasr_priv); +		vgasr_priv.active = false; +	}  	mutex_unlock(&vgasr_mutex);  }  EXPORT_SYMBOL(vga_switcheroo_unregister_handler); -static void vga_switcheroo_enable(void) +static int register_client(struct pci_dev *pdev, +			   const struct vga_switcheroo_client_ops *ops, +			   int id, bool active, bool driver_power_control)  { -	int i; -	int ret; -	/* call the handler to init */ -	vgasr_priv.handler->init(); +	struct vga_switcheroo_client *client; -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev); -		if (ret < 0) -			return; - -		vgasr_priv.clients[i].id = ret; -	} -	vga_switcheroo_debugfs_init(&vgasr_priv); -	vgasr_priv.active = true; -} +	client = kzalloc(sizeof(*client), GFP_KERNEL); +	if (!client) +		return -ENOMEM; -int vga_switcheroo_register_client(struct pci_dev *pdev, -				   void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state), -				   bool (*can_switch)(struct pci_dev *pdev)) -{ -	int index; +	client->pwr_state = VGA_SWITCHEROO_ON; +	client->pdev = pdev; +	client->ops = ops; +	client->id = id; +	client->active = active; +	client->driver_power_control = driver_power_control;  	mutex_lock(&vgasr_mutex); -	/* don't do IGD vs DIS here */ -	if (vgasr_priv.registered_clients & 1) -		index = 1; -	else -		index = 0; - -	vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON; -	vgasr_priv.clients[index].pdev = pdev; -	vgasr_priv.clients[index].set_gpu_state = set_gpu_state; -	vgasr_priv.clients[index].can_switch = can_switch; -	vgasr_priv.clients[index].id = -1; -	if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) -		vgasr_priv.clients[index].active = true; - -	vgasr_priv.registered_clients |= (1 << index); - -	/* if we get two clients + handler */ -	if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) { +	list_add_tail(&client->list, &vgasr_priv.clients); +	if (client_is_vga(client)) +		vgasr_priv.registered_clients++; + +	if (vga_switcheroo_ready()) {  		printk(KERN_INFO "vga_switcheroo: enabled\n");  		vga_switcheroo_enable();  	}  	mutex_unlock(&vgasr_mutex);  	return 0;  } + +int vga_switcheroo_register_client(struct pci_dev *pdev, +				   const struct vga_switcheroo_client_ops *ops, +				   bool driver_power_control) +{ +	return register_client(pdev, ops, -1, +			       pdev == vga_default_device(), driver_power_control); +}  EXPORT_SYMBOL(vga_switcheroo_register_client); +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, false); +} +EXPORT_SYMBOL(vga_switcheroo_register_audio_client); + +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) +		if (client->active && client_is_vga(client)) +			return client; +	return NULL; +} + +int vga_switcheroo_get_client_state(struct pci_dev *pdev) +{ +	struct vga_switcheroo_client *client; + +	client = find_client_from_pci(&vgasr_priv.clients, pdev); +	if (!client) +		return VGA_SWITCHEROO_NOT_FOUND; +	if (!vgasr_priv.active) +		return VGA_SWITCHEROO_INIT; +	return client->pwr_state; +} +EXPORT_SYMBOL(vga_switcheroo_get_client_state); +  void vga_switcheroo_unregister_client(struct pci_dev *pdev)  { -	int i; +	struct vga_switcheroo_client *client;  	mutex_lock(&vgasr_mutex); -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		if (vgasr_priv.clients[i].pdev == pdev) { -			vgasr_priv.registered_clients &= ~(1 << i); -			break; -		} +	client = find_client_from_pci(&vgasr_priv.clients, pdev); +	if (client) { +		if (client_is_vga(client)) +			vgasr_priv.registered_clients--; +		list_del(&client->list); +		kfree(client); +	} +	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;  	} - -	printk(KERN_INFO "vga_switcheroo: disabled\n"); -	vga_switcheroo_debugfs_fini(&vgasr_priv); -	vgasr_priv.active = false;  	mutex_unlock(&vgasr_mutex);  }  EXPORT_SYMBOL(vga_switcheroo_unregister_client); @@ -156,28 +246,30 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_client);  void vga_switcheroo_client_fb_set(struct pci_dev *pdev,  				 struct fb_info *info)  { -	int i; +	struct vga_switcheroo_client *client;  	mutex_lock(&vgasr_mutex); -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		if (vgasr_priv.clients[i].pdev == pdev) { -			vgasr_priv.clients[i].fb_info = info; -			break; -		} -	} +	client = find_client_from_pci(&vgasr_priv.clients, pdev); +	if (client) +		client->fb_info = info;  	mutex_unlock(&vgasr_mutex);  }  EXPORT_SYMBOL(vga_switcheroo_client_fb_set);  static int vga_switcheroo_show(struct seq_file *m, void *v)  { -	int i; +	struct vga_switcheroo_client *client; +	int i = 0;  	mutex_lock(&vgasr_mutex); -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		seq_printf(m, "%d:%c:%s:%s\n", i, -			   vgasr_priv.clients[i].active ? '+' : ' ', -			   vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off", -			   pci_name(vgasr_priv.clients[i].pdev)); +	list_for_each_entry(client, &vgasr_priv.clients, list) { +		seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i, +			   client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", +			   client_is_vga(client) ? "" : "-Audio", +			   client->active ? '+' : ' ', +			   client->driver_power_control ? "Dyn" : "", +			   client->pwr_state ? "Pwr" : "Off", +			   pci_name(client->pdev)); +		i++;  	}  	mutex_unlock(&vgasr_mutex);  	return 0; @@ -190,81 +282,114 @@ static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)  static int vga_switchon(struct vga_switcheroo_client *client)  { -	int ret; - -	ret = vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); +	if (client->driver_power_control) +		return 0; +	if (vgasr_priv.handler->power_state) +		vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);  	/* call the driver callback to turn on device */ -	client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); +	client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON);  	client->pwr_state = VGA_SWITCHEROO_ON;  	return 0;  }  static int vga_switchoff(struct vga_switcheroo_client *client)  { +	if (client->driver_power_control) +		return 0;  	/* call the driver callback to turn off device */ -	client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); -	vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); +	client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); +	if (vgasr_priv.handler->power_state) +		vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF);  	client->pwr_state = VGA_SWITCHEROO_OFF;  	return 0;  } -static int vga_switchto(struct vga_switcheroo_client *new_client) +static void set_audio_state(int id, int state)  { -	int ret; -	int i; -	struct vga_switcheroo_client *active = NULL; - -	if (new_client->active == true) -		return 0; +	struct vga_switcheroo_client *client; -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		if (vgasr_priv.clients[i].active == true) { -			active = &vgasr_priv.clients[i]; -			break; -		} +	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;  	} +} + +/* stage one happens before delay */ +static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) +{ +	struct vga_switcheroo_client *active; + +	active = find_active_client(&vgasr_priv.clients);  	if (!active)  		return 0; -	/* power up the first device */ -	ret = pci_enable_device(new_client->pdev); -	if (ret) -		return ret; -  	if (new_client->pwr_state == VGA_SWITCHEROO_OFF)  		vga_switchon(new_client); -	/* swap shadow resource to denote boot VGA device has changed so X starts on new device */ +	vga_set_default_device(new_client->pdev); +	return 0; +} + +/* post delay */ +static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) +{ +	int ret; +	struct vga_switcheroo_client *active; + +	active = find_active_client(&vgasr_priv.clients); +	if (!active) +		return 0; +  	active->active = false; -	active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW; -	new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; +	set_audio_state(active->id, VGA_SWITCHEROO_OFF);  	if (new_client->fb_info) {  		struct fb_event event; +		console_lock();  		event.info = new_client->fb_info;  		fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); +		console_unlock();  	}  	ret = vgasr_priv.handler->switchto(new_client->id);  	if (ret)  		return ret; +	if (new_client->ops->reprobe) +		new_client->ops->reprobe(new_client->pdev); +  	if (active->pwr_state == VGA_SWITCHEROO_ON)  		vga_switchoff(active); +	set_audio_state(new_client->id, VGA_SWITCHEROO_ON); +  	new_client->active = true;  	return 0;  } +static bool check_can_switch(void) +{ +	struct vga_switcheroo_client *client; + +	list_for_each_entry(client, &vgasr_priv.clients, list) { +		if (!client->ops->can_switch(client->pdev)) { +			printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id); +			return false; +		} +	} +	return true; +} +  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; -	int i, ret; +	int ret;  	bool delay = false, can_switch; +	bool just_mux = false;  	int client_id = -1;  	struct vga_switcheroo_client *client = NULL; @@ -283,21 +408,27 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,  	/* pwr off the device not in use */  	if (strncmp(usercmd, "OFF", 3) == 0) { -		for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -			if (vgasr_priv.clients[i].active) +		list_for_each_entry(client, &vgasr_priv.clients, list) { +			if (client->active || client_is_audio(client))  				continue; -			if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON) -				vga_switchoff(&vgasr_priv.clients[i]); +			if (client->driver_power_control) +				continue; +			set_audio_state(client->id, VGA_SWITCHEROO_OFF); +			if (client->pwr_state == VGA_SWITCHEROO_ON) +				vga_switchoff(client);  		}  		goto out;  	}  	/* pwr on the device not in use */  	if (strncmp(usercmd, "ON", 2) == 0) { -		for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -			if (vgasr_priv.clients[i].active) +		list_for_each_entry(client, &vgasr_priv.clients, list) { +			if (client->active || client_is_audio(client)) +				continue; +			if (client->driver_power_control)  				continue; -			if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF) -				vga_switchon(&vgasr_priv.clients[i]); +			if (client->pwr_state == VGA_SWITCHEROO_OFF) +				vga_switchon(client); +			set_audio_state(client->id, VGA_SWITCHEROO_ON);  		}  		goto out;  	} @@ -319,44 +450,54 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,  	if (strncmp(usercmd, "DIS", 3) == 0)  		client_id = VGA_SWITCHEROO_DIS; +	if (strncmp(usercmd, "MIGD", 4) == 0) { +		just_mux = true; +		client_id = VGA_SWITCHEROO_IGD; +	} +	if (strncmp(usercmd, "MDIS", 4) == 0) { +		just_mux = true; +		client_id = VGA_SWITCHEROO_DIS; +	} +  	if (client_id == -1)  		goto out; +	client = find_client_from_id(&vgasr_priv.clients, client_id); +	if (!client) +		goto out; -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		if (vgasr_priv.clients[i].id == client_id) { -			client = &vgasr_priv.clients[i]; -			break; -		} +	vgasr_priv.delayed_switch_active = false; + +	if (just_mux) { +		ret = vgasr_priv.handler->switchto(client_id); +		goto out;  	} -	vgasr_priv.delayed_switch_active = false; +	if (client->active) +		goto out; +  	/* okay we want a switch - test if devices are willing to switch */ -	can_switch = true; -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); -		if (can_switch == false) { -			printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); -			break; -		} -	} +	can_switch = check_can_switch();  	if (can_switch == false && delay == false)  		goto out; -	if (can_switch == true) { -		pdev_name = pci_name(client->pdev); -		ret = vga_switchto(client); +	if (can_switch) { +		ret = vga_switchto_stage1(client);  		if (ret) -			printk(KERN_ERR "vga_switcheroo: switching failed %d\n", ret); +			printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret); + +		ret = vga_switchto_stage2(client); +		if (ret) +			printk(KERN_ERR "vga_switcheroo: switching failed stage 2 %d\n", ret); +  	} 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; -		/* we should at least power up the card to -		   make the switch faster */ -		if (client->pwr_state == VGA_SWITCHEROO_OFF) -			vga_switchon(client); +		ret = vga_switchto_stage1(client); +		if (ret) +			printk(KERN_ERR "vga_switcheroo: delayed switching stage 1 failed %d\n", ret);  	}  out: @@ -411,10 +552,7 @@ fail:  int vga_switcheroo_process_delayed_switch(void)  { -	struct vga_switcheroo_client *client = NULL; -	const char *pdev_name; -	bool can_switch = true; -	int i; +	struct vga_switcheroo_client *client;  	int ret;  	int err = -EINVAL; @@ -424,23 +562,14 @@ int vga_switcheroo_process_delayed_switch(void)  	printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); -	for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { -		if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id) -			client = &vgasr_priv.clients[i]; -		can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); -		if (can_switch == false) { -			printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); -			break; -		} -	} - -	if (can_switch == false || client == NULL) +	client = find_client_from_id(&vgasr_priv.clients, +				     vgasr_priv.delayed_client_id); +	if (!client || !check_can_switch())  		goto err; -	pdev_name = pci_name(client->pdev); -	ret = vga_switchto(client); +	ret = vga_switchto_stage2(client);  	if (ret) -		printk(KERN_ERR "vga_switcheroo: delayed switching failed %d\n", ret); +		printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret);  	vgasr_priv.delayed_switch_active = false;  	err = 0; @@ -450,3 +579,127 @@ err:  }  EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); +static void vga_switcheroo_power_switch(struct pci_dev *pdev, enum vga_switcheroo_state state) +{ +	struct vga_switcheroo_client *client; + +	if (!vgasr_priv.handler->power_state) +		return; + +	client = find_client_from_pci(&vgasr_priv.clients, pdev); +	if (!client) +		return; + +	if (!client->driver_power_control) +		return; + +	vgasr_priv.handler->power_state(client->id, state); +} + +/* force a PCI device to a certain state - mainly to turn off audio clients */ + +void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) +{ +	struct vga_switcheroo_client *client; + +	client = find_client_from_pci(&vgasr_priv.clients, pdev); +	if (!client) +		return; + +	if (!client->driver_power_control) +		return; + +	client->pwr_state = dynamic; +	set_audio_state(client->id, dynamic); +} +EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch); + +/* switcheroo power domain */ +static int vga_switcheroo_runtime_suspend(struct device *dev) +{ +	struct pci_dev *pdev = to_pci_dev(dev); +	int ret; + +	ret = dev->bus->pm->runtime_suspend(dev); +	if (ret) +		return ret; +	if (vgasr_priv.handler->switchto) +		vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD); +	vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF); +	return 0; +} + +static int vga_switcheroo_runtime_resume(struct device *dev) +{ +	struct pci_dev *pdev = to_pci_dev(dev); +	int ret; + +	vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_ON); +	ret = dev->bus->pm->runtime_resume(dev); +	if (ret) +		return ret; + +	return 0; +} + +/* this version is for the case where the power switch is separate +   to the device being powered down. */ +int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) +{ +	/* copy over all the bus versions */ +	if (dev->bus && dev->bus->pm) { +		domain->ops = *dev->bus->pm; +		domain->ops.runtime_suspend = vga_switcheroo_runtime_suspend; +		domain->ops.runtime_resume = vga_switcheroo_runtime_resume; + +		dev->pm_domain = domain; +		return 0; +	} +	dev->pm_domain = NULL; +	return -EINVAL; +} +EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops); + +static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) +{ +	struct pci_dev *pdev = to_pci_dev(dev); +	int ret; +	struct vga_switcheroo_client *client, *found = NULL; + +	/* we need to check if we have to switch back on the video +	   device so the audio device can come back */ +	list_for_each_entry(client, &vgasr_priv.clients, list) { +		if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && client_is_vga(client)) { +			found = client; +			ret = pm_runtime_get_sync(&client->pdev->dev); +			if (ret) { +				if (ret != 1) +					return ret; +			} +			break; +		} +	} +	ret = dev->bus->pm->runtime_resume(dev); + +	/* put the reference for the gpu */ +	if (found) { +		pm_runtime_mark_last_busy(&found->pdev->dev); +		pm_runtime_put_autosuspend(&found->pdev->dev); +	} +	return ret; +} + +int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) +{ +	/* copy over all the bus versions */ +	if (dev->bus && dev->bus->pm) { +		domain->ops = *dev->bus->pm; +		domain->ops.runtime_resume = vga_switcheroo_runtime_resume_hdmi_audio; + +		dev->pm_domain = domain; +		return 0; +	} +	dev->pm_domain = NULL; +	return -EINVAL; +} +EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio);  | 
