Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fe3f58c15f | ||
|
fb3d5a614a | ||
|
fd774b1f5e | ||
|
daa6411911 | ||
|
915413ab92 |
@ -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.
|
||||
|
34
bbswitch.c
34
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
|
||||
@ -197,6 +207,26 @@ static int bbswitch_optimus_dsm(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Windows 8/8.1/10 do not use DSM to put the device in D3cold state,
|
||||
// instead it disables power resources on the parent PCIe port device.
|
||||
static bool has_pr3_support(void) {
|
||||
acpi_handle parent_handle;
|
||||
struct acpi_device *ad = NULL;
|
||||
|
||||
if (ACPI_FAILURE(acpi_get_parent(dis_handle, &parent_handle))) {
|
||||
pr_warn("Failed to obtain the parent device\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
acpi_bus_get_device(parent_handle, &ad);
|
||||
if (!ad) {
|
||||
pr_warn("Failed to obtain an ACPI device for handle\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ad->power.flags.power_resources;
|
||||
}
|
||||
|
||||
static int bbswitch_acpi_off(void) {
|
||||
if (dsm_type == DSM_TYPE_NVIDIA) {
|
||||
char args[] = {2, 0, 0, 0};
|
||||
@ -436,7 +466,9 @@ static int __init bbswitch_init(void) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!skip_optimus_dsm &&
|
||||
if (has_pr3_support()) {
|
||||
pr_info("skipping _DSM as _PR3 support is detected\n");
|
||||
} else if (!skip_optimus_dsm &&
|
||||
has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) {
|
||||
dsm_type = DSM_TYPE_OPTIMUS;
|
||||
pr_info("detected an Optimus _DSM function\n");
|
||||
|
100
bbswitch_dev.c
Normal file
100
bbswitch_dev.c
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Toggle the discrete graphics card (PCI driver)");
|
||||
MODULE_AUTHOR("Peter Wu <peter@lekensteyn.nl>");
|
||||
|
||||
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 */
|
||||
/* Use autosuspend to avoid lspci waking up the device multiple times. */
|
||||
pm_runtime_set_autosuspend_delay(&dev->dev, 2000);
|
||||
pm_runtime_use_autosuspend(&dev->dev);
|
||||
pm_runtime_allow(&dev->dev);
|
||||
pm_runtime_put_autosuspend(&dev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bbswitch_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
pm_runtime_get_noresume(&dev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&dev->dev);
|
||||
pm_runtime_forbid(&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);
|
||||
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. */
|
||||
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: */
|
Loading…
x
Reference in New Issue
Block a user