Start handling audio devices via vga_switcheroo
This is an attempt to notify the audio driver (snd-hda-intel) that the graphics card is being disabled to prevent access to a sleeping device. TODO: - System suspend/resume is not really working properly with this. Addresses https://github.com/Bumblebee-Project/bbswitch/issues/18
This commit is contained in:
		
							
								
								
									
										53
									
								
								bbswitch.c
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								bbswitch.c
									
									
									
									
									
								
							@@ -36,6 +36,7 @@
 | 
				
			|||||||
#include <linux/seq_file.h>
 | 
					#include <linux/seq_file.h>
 | 
				
			||||||
#include <linux/pm_runtime.h>
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
#include <linux/pm_domain.h>
 | 
					#include <linux/pm_domain.h>
 | 
				
			||||||
 | 
					#include <linux/vga_switcheroo.h>
 | 
				
			||||||
#include <linux/version.h>
 | 
					#include <linux/version.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
 | 
					#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
 | 
				
			||||||
@@ -290,6 +291,9 @@ static int bbswitch_pci_runtime_suspend(struct device *dev)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    pr_info("disabling discrete graphics\n");
 | 
					    pr_info("disabling discrete graphics\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Ensure that the audio driver knows not to touch us. */
 | 
				
			||||||
 | 
					    vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bbswitch_optimus_dsm();
 | 
					    bbswitch_optimus_dsm();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Save state now that the device is still awake, makes PCI layer happy */
 | 
					    /* Save state now that the device is still awake, makes PCI layer happy */
 | 
				
			||||||
@@ -301,8 +305,12 @@ static int bbswitch_pci_runtime_suspend(struct device *dev)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static int bbswitch_pci_runtime_resume(struct device *dev)
 | 
					static int bbswitch_pci_runtime_resume(struct device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    struct pci_dev *pdev = to_pci_dev(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pr_debug("Finishing runtime resume.\n");
 | 
					    pr_debug("Finishing runtime resume.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Resume audio driver. */
 | 
				
			||||||
 | 
					    vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -312,6 +320,43 @@ static const struct dev_pm_ops bbswitch_pci_pm_ops = {
 | 
				
			|||||||
    /* No runtime_idle callback, the default zero delay is sufficient. */
 | 
					    /* No runtime_idle callback, the default zero delay is sufficient. */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int bbswitch_switcheroo_switchto(enum vga_switcheroo_client_id id)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* We do not support switching, only power on/off. */
 | 
				
			||||||
 | 
					    return -ENOSYS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static enum vga_switcheroo_client_id bbswitch_switcheroo_get_client_id(struct pci_dev *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* Our registered client is always the discrete GPU. */
 | 
				
			||||||
 | 
					    return VGA_SWITCHEROO_DIS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct vga_switcheroo_handler bbswitch_handler = {
 | 
				
			||||||
 | 
					    .switchto = bbswitch_switcheroo_switchto,
 | 
				
			||||||
 | 
					    .get_client_id = bbswitch_switcheroo_get_client_id,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void bbswitch_switcheroo_set_gpu_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* Nothing to do, we handle the PM domain ourselves. Perhaps we can add
 | 
				
			||||||
 | 
					     * backwards compatibility with older kernels in this way and workaround
 | 
				
			||||||
 | 
					     * bugs? */
 | 
				
			||||||
 | 
					    pr_debug("set_gpu_state to %s\n", state == VGA_SWITCHEROO_ON ? "ON" : "OFF");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool bbswitch_switcheroo_can_switch(struct pci_dev *pdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* We do not support switching between IGD/DIS. */
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct vga_switcheroo_client_ops bbswitch_switcheroo_ops = {
 | 
				
			||||||
 | 
					    .set_gpu_state = bbswitch_switcheroo_set_gpu_state,
 | 
				
			||||||
 | 
					    .can_switch = bbswitch_switcheroo_can_switch,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int bbswitch_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 | 
					static int bbswitch_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /* Only bind to the device which we discovered before. */
 | 
					    /* Only bind to the device which we discovered before. */
 | 
				
			||||||
@@ -322,6 +367,12 @@ static int bbswitch_pci_probe(struct pci_dev *pdev, const struct pci_device_id *
 | 
				
			|||||||
    dis_dev = pdev;
 | 
					    dis_dev = pdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bbswitch_pmd_set(&pdev->dev);
 | 
					    bbswitch_pmd_set(&pdev->dev);
 | 
				
			||||||
 | 
					#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)
 | 
				
			||||||
 | 
					    vga_switcheroo_register_handler(&bbswitch_handler, 0);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    vga_switcheroo_register_handler(&bbswitch_handler);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    vga_switcheroo_register_client(pdev, &bbswitch_switcheroo_ops, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Prevent kernel from detaching the PCI device for some devices that
 | 
					    /* Prevent kernel from detaching the PCI device for some devices that
 | 
				
			||||||
     * generate hotplug events. The graphics card is typically not physically
 | 
					     * generate hotplug events. The graphics card is typically not physically
 | 
				
			||||||
@@ -345,6 +396,8 @@ static void bbswitch_pci_remove(struct pci_dev *pdev)
 | 
				
			|||||||
    pm_runtime_dont_use_autosuspend(&pdev->dev);
 | 
					    pm_runtime_dont_use_autosuspend(&pdev->dev);
 | 
				
			||||||
    pm_runtime_forbid(&pdev->dev);
 | 
					    pm_runtime_forbid(&pdev->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vga_switcheroo_unregister_client(pdev);
 | 
				
			||||||
 | 
					    vga_switcheroo_unregister_handler();
 | 
				
			||||||
    dev_pm_domain_set(&pdev->dev, NULL);
 | 
					    dev_pm_domain_set(&pdev->dev, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    dis_dev = NULL;
 | 
					    dis_dev = NULL;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user