diff --git a/bbswitch.c b/bbswitch.c index f0c719b..69ed4c9 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #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"); + /* Ensure that the audio driver knows not to touch us. */ + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); + bbswitch_optimus_dsm(); /* 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) { + struct pci_dev *pdev = to_pci_dev(dev); + pr_debug("Finishing runtime resume.\n"); + /* Resume audio driver. */ + vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); 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. */ }; +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) { /* 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; 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 * 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_forbid(&pdev->dev); + vga_switcheroo_unregister_client(pdev); + vga_switcheroo_unregister_handler(); dev_pm_domain_set(&pdev->dev, NULL); dis_dev = NULL;