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
|
Upload the generated tarball on the above Launchpad URL and provide a link to
|
||||||
the comment containing your report.
|
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
|
* # echo ON > /proc/acpi/bbswitch
|
||||||
* Get status
|
* Get status
|
||||||
* # cat /proc/acpi/bbswitch
|
* # 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
|
* Copyright (C) 2011-2013 Bumblebee Project
|
||||||
@ -197,6 +207,26 @@ static int bbswitch_optimus_dsm(void) {
|
|||||||
return 0;
|
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) {
|
static int bbswitch_acpi_off(void) {
|
||||||
if (dsm_type == DSM_TYPE_NVIDIA) {
|
if (dsm_type == DSM_TYPE_NVIDIA) {
|
||||||
char args[] = {2, 0, 0, 0};
|
char args[] = {2, 0, 0, 0};
|
||||||
@ -436,7 +466,9 @@ static int __init bbswitch_init(void) {
|
|||||||
return -ENODEV;
|
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)) {
|
has_dsm_func(acpi_optimus_dsm_muid, 0x100, 0x1A)) {
|
||||||
dsm_type = DSM_TYPE_OPTIMUS;
|
dsm_type = DSM_TYPE_OPTIMUS;
|
||||||
pr_info("detected an Optimus _DSM function\n");
|
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