diff --git a/README.md b/README.md index 11ad3d7..2875a48 100644 --- a/README.md +++ b/README.md @@ -165,3 +165,9 @@ issues on this module in the issue tracker and provide the following details: Upload the generated tarball on the above Launchpad URL and provide a link to the comment containing your report. + +TODO +---- +With the new PCI device approach, if load_state=0, then starting bumblebeed will +unload bbswitch. Fix Bumblebee not to unload the module when the PM method is +bbswitch and the loaded module is bbswitch. diff --git a/bbswitch.c b/bbswitch.c index 975794a..a5c4a9e 100644 --- a/bbswitch.c +++ b/bbswitch.c @@ -8,6 +8,16 @@ * # echo ON > /proc/acpi/bbswitch * Get status * # cat /proc/acpi/bbswitch + * + * Note: only one PCI driver (bbswitch, nouveau, etc.) can bind to a PCI device. + * When turning a device OFF, bbswitch tries to bind itself to the PCI device. + * When turning a device ON, bbswitch unbinds a device (if it was bound) before + * returning from the write. + * + * TODO is this true? + * The new dual module approach is used to allow for backwards compatibility, + * Bumblebee unloads any driver that is loaded for a device, that would however + * result in unloading the main bbswitch module. */ /* * Copyright (C) 2011-2013 Bumblebee Project diff --git a/bbswitch_dev.c b/bbswitch_dev.c new file mode 100644 index 0000000..a00b5b6 --- /dev/null +++ b/bbswitch_dev.c @@ -0,0 +1,97 @@ +/* + * TODO merge into main bbswitch module. + * TODO on ON call device_release_driver + * TODO how to bind to a specific device from kernel space? Can't use + * driver_probe_device (https://lkml.org/lkml/2014/2/14/628). Maybe use + * driver_override or new_id/bind/remove_id from userspace? + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Toggle the discrete graphics card (PCI driver)"); +MODULE_AUTHOR("Peter Wu "); + +static int bbswitch_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + /* TODO how to discover devices? */ + /* Prevent kernel from detaching the PCI device for some devices that + * generate hotplug events. The graphics card is typically not physically + * removable. */ + pci_ignore_hotplug(dev); + + pm_runtime_set_active(&dev->dev); /* clear any errors */ + pm_runtime_allow(&dev->dev); + pm_runtime_put_noidle(&dev->dev); + return 0; +} + +static void bbswitch_pci_remove(struct pci_dev *dev) +{ + pm_runtime_get_noresume(&dev->dev); +} + +static int bbswitch_runtime_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); + + pr_info("disabling discrete graphics\n"); + + /* TODO if _PR3 is not supported, call Optimus DSM here. */ + /* TODO for v1 Optimus, call DSM here. */ + + /* Save state now that the device is still awake, makes PCI layer happy */ + pci_save_state(pdev); + /* TODO if _PR3 is supported, should this be PCI_D3hot? */ + pci_set_power_state(pdev, PCI_D3cold); + mdelay(1000); + return 0; +} + +static int bbswitch_runtime_resume(struct device *dev) { + pr_info("enabling discrete graphics\n"); + + /* TODO for v1 Optimus, call DSM here. */ + /* Nothing to do for Optimus, the PCI layer already moved into D0 state. */ + mdelay(1000); + return 0; +} + +static struct dev_pm_ops bbswitch_pm_ops = { + .runtime_suspend = bbswitch_runtime_suspend, + .runtime_resume = bbswitch_runtime_resume, + /* No runtime_idle callback, the default zero delay is sufficient. */ +}; + +static struct pci_driver bbswitch_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = NULL, /* will be added dynamically */ + .probe = bbswitch_pci_probe, + .remove = bbswitch_pci_remove, + .driver.pm = &bbswitch_pm_ops, +}; + +static int __init bbswitch_dev_init(void) { + int ret; + + ret = pci_register_driver(&bbswitch_pci_driver); +#if 0 + ret = pci_add_dynid(&bbswitch_pci_driver, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16, 0xff0000); +#endif + + return ret; +} + +static void __exit bbswitch_dev_exit(void) { + pci_unregister_driver(&bbswitch_pci_driver); +} + +module_init(bbswitch_dev_init); +module_exit(bbswitch_dev_exit); + +/* vim: set sw=4 ts=4 et: */