123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- From ff7a7f5931f8f0aae029daa36209686818aafbe1 Mon Sep 17 00:00:00 2001
- From: Maximilian Luz <luzmaximilian@gmail.com>
- Date: Wed, 3 Jul 2019 00:35:24 +0200
- Subject: [PATCH 12/12] surfacebook2-dgpu
- ---
- drivers/platform/x86/Kconfig | 9 +
- drivers/platform/x86/Makefile | 1 +
- drivers/platform/x86/surfacebook2_dgpu_hps.c | 306 +++++++++++++++++++
- 3 files changed, 316 insertions(+)
- create mode 100644 drivers/platform/x86/surfacebook2_dgpu_hps.c
- diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
- index 21ffc03cd372..200d4d55da98 100644
- --- a/drivers/platform/x86/Kconfig
- +++ b/drivers/platform/x86/Kconfig
- @@ -483,6 +483,15 @@ config SURFACE3_WMI
- To compile this driver as a module, choose M here: the module will
- be called surface3-wmi.
-
- +config SURFACE_BOOK2_DGPU_HPS
- + tristate "Surface Book 2 dGPU Hot-Plug System Driver"
- + depends on ACPI
- + ---help---
- + This is an experimetnal driver to control the power-state of the
- + Surface Book 2 dGPU.
- +
- + If you have a Surface Book 2, say Y or M here.
- +
- config THINKPAD_ACPI
- tristate "ThinkPad ACPI Laptop Extras"
- depends on ACPI
- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
- index 278ce32df99f..0faf98020b84 100644
- --- a/drivers/platform/x86/Makefile
- +++ b/drivers/platform/x86/Makefile
- @@ -49,6 +49,7 @@ obj-$(CONFIG_ACPI_WMI) += wmi.o
- obj-$(CONFIG_MSI_WMI) += msi-wmi.o
- obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
- obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
- +obj-$(CONFIG_SURFACE_BOOK2_DGPU_HPS) += surfacebook2_dgpu_hps.o
- obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
- obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
- obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
- diff --git a/drivers/platform/x86/surfacebook2_dgpu_hps.c b/drivers/platform/x86/surfacebook2_dgpu_hps.c
- new file mode 100644
- index 000000000000..7639fb0029d8
- --- /dev/null
- +++ b/drivers/platform/x86/surfacebook2_dgpu_hps.c
- @@ -0,0 +1,306 @@
- +#include <linux/acpi.h>
- +#include <linux/gpio.h>
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/moduleparam.h>
- +#include <linux/platform_device.h>
- +#include <linux/sysfs.h>
- +
- +#include <linux/uaccess.h>
- +
- +
- +#define SB2_SHPS_DSM_REVISION 1
- +#define SB2_SHPS_DSM_GPU_STATE 0x05
- +
- +static const guid_t SB2_SHPS_DSM_UUID =
- + GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd,
- + 0x32, 0x0e, 0x10, 0x36, 0x0a);
- +
- +#define SB2_PARAM_PERM (S_IRUGO | S_IWUSR)
- +
- +
- +static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false };
- +static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false };
- +static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false };
- +static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false };
- +static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false };
- +static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false };
- +
- +static const struct acpi_gpio_mapping sb2_mshw0153_acpi_gpios[] = {
- + { "base_presence-int-gpio", &gpio_base_presence_int, 1 },
- + { "base_presence-gpio", &gpio_base_presence, 1 },
- + { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 },
- + { "dgpu_power-gpio", &gpio_dgpu_power, 1 },
- + { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 },
- + { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 },
- + { },
- +};
- +
- +
- +enum sb2_dgpu_power {
- + SB2_DGPU_POWER_OFF = 0,
- + SB2_DGPU_POWER_ON = 1,
- +
- + __SB2_DGPU_POWER__START = 0,
- + __SB2_DGPU_POWER__END = 1,
- +};
- +
- +enum sb2_param_dgpu_power {
- + SB2_PARAM_DGPU_POWER_OFF = SB2_DGPU_POWER_OFF,
- + SB2_PARAM_DGPU_POWER_ON = SB2_DGPU_POWER_ON,
- + SB2_PARAM_DGPU_POWER_AS_IS = 2,
- +
- + __SB2_PARAM_DGPU_POWER__START = 0,
- + __SB2_PARAM_DGPU_POWER__END = 2,
- +};
- +
- +static const char* sb2_dgpu_power_str(enum sb2_dgpu_power power) {
- + if (power == SB2_DGPU_POWER_OFF) {
- + return "off";
- + } else if (power == SB2_DGPU_POWER_ON) {
- + return "on";
- + } else {
- + return "<invalid>";
- + }
- +}
- +
- +
- +struct sb2_shps_driver_data {
- + struct mutex dgpu_power_lock;
- + enum sb2_dgpu_power dgpu_power;
- +};
- +
- +
- +static int __sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power)
- +{
- + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
- + acpi_handle handle = ACPI_HANDLE(&pdev->dev);
- + union acpi_object *result;
- + union acpi_object param;
- +
- + param.type = ACPI_TYPE_INTEGER;
- + param.integer.value = power == SB2_DGPU_POWER_ON;
- +
- + result = acpi_evaluate_dsm_typed(handle, &SB2_SHPS_DSM_UUID, SB2_SHPS_DSM_REVISION,
- + SB2_SHPS_DSM_GPU_STATE, ¶m, ACPI_TYPE_BUFFER);
- +
- + if (IS_ERR_OR_NULL(result)) {
- + return result ? PTR_ERR(result) : -EFAULT;
- + }
- +
- + if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) {
- + return -EIO;
- + }
- +
- + drvdata->dgpu_power = power;
- +
- + printk(KERN_INFO "sb2_shps: dGPU power state set to \'%s\'\n", sb2_dgpu_power_str(power));
- +
- + ACPI_FREE(result);
- + return 0;
- +}
- +
- +static int sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power)
- +{
- + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
- + int status = 0;
- +
- + if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) {
- + return -EINVAL;
- + }
- +
- + mutex_lock(&drvdata->dgpu_power_lock);
- + if (power != drvdata->dgpu_power) {
- + status = __sb2_shps_dgpu_set_power(pdev, power);
- + }
- + mutex_unlock(&drvdata->dgpu_power_lock);
- +
- + return status;
- +}
- +
- +static int sb2_shps_dgpu_force_power(struct platform_device *pdev, enum sb2_dgpu_power power)
- +{
- + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
- + int status;
- +
- + if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) {
- + return -EINVAL;
- + }
- +
- + mutex_lock(&drvdata->dgpu_power_lock);
- + status = __sb2_shps_dgpu_set_power(pdev, power);
- + mutex_unlock(&drvdata->dgpu_power_lock);
- +
- + return status;
- +}
- +
- +
- +static int param_dgpu_power_set(const char *val, const struct kernel_param *kp)
- +{
- + int power = SB2_PARAM_DGPU_POWER_OFF;
- + int status;
- +
- + status = kstrtoint(val, 0, &power);
- + if (status) {
- + return status;
- + }
- +
- + if (power < __SB2_PARAM_DGPU_POWER__START || power > __SB2_PARAM_DGPU_POWER__END) {
- + return -EINVAL;
- + }
- +
- + return param_set_int(val, kp);
- +}
- +
- +static const struct kernel_param_ops param_dgpu_power_ops = {
- + .set = param_dgpu_power_set,
- + .get = param_get_int,
- +};
- +
- +static int param_dgpu_power_init = SB2_PARAM_DGPU_POWER_OFF;
- +static int param_dgpu_power_exit = SB2_PARAM_DGPU_POWER_OFF;
- +
- +module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SB2_PARAM_PERM);
- +module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SB2_PARAM_PERM);
- +
- +MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is)");
- +MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is)");
- +
- +
- +static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data)
- +{
- + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
- + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
- +
- + return sprintf(data, "%s\n", sb2_dgpu_power_str(drvdata->dgpu_power));
- +}
- +
- +static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr,
- + const char *data, size_t count)
- +{
- + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
- + bool power = false;
- + int status;
- +
- + status = kstrtobool(data, &power);
- + if (status) {
- + return status;
- + }
- +
- + if (power) {
- + status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_ON);
- + } else {
- + status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_OFF);
- + }
- +
- + return status < 0 ? status : count;
- +}
- +
- +const static DEVICE_ATTR_RW(dgpu_power);
- +
- +
- +#ifdef CONFIG_PM
- +
- +static int sb2_shps_resume(struct device *dev)
- +{
- + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
- + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
- +
- + return sb2_shps_dgpu_force_power(pdev, drvdata->dgpu_power);
- +}
- +
- +static SIMPLE_DEV_PM_OPS(sb2_shps_pm_ops, NULL, sb2_shps_resume);
- +
- +#endif
- +
- +
- +static int sb2_shps_probe(struct platform_device *pdev)
- +{
- + struct sb2_shps_driver_data *drvdata;
- + struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev);
- + int status = 0;
- +
- + if (gpiod_count(&pdev->dev, NULL) < 0) {
- + return -ENODEV;
- + }
- +
- + status = acpi_dev_add_driver_gpios(shps_dev, sb2_mshw0153_acpi_gpios);
- + if (status) {
- + return status;
- + }
- +
- + drvdata = kzalloc(sizeof(struct sb2_shps_driver_data), GFP_KERNEL);
- + if (!drvdata) {
- + status = -ENOMEM;
- + goto err_alloc_drvdata;
- + }
- +
- + mutex_init(&drvdata->dgpu_power_lock);
- + drvdata->dgpu_power = SB2_DGPU_POWER_OFF;
- + platform_set_drvdata(pdev, drvdata);
- +
- + if (param_dgpu_power_init != SB2_PARAM_DGPU_POWER_AS_IS) {
- + status = sb2_shps_dgpu_force_power(pdev, param_dgpu_power_init);
- + if (status) {
- + goto err_set_power;
- + }
- + }
- +
- + status = sysfs_create_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr);
- + if (status) {
- + goto err_sysfs;
- + }
- +
- + return 0;
- +
- +err_sysfs:
- + sb2_shps_dgpu_force_power(pdev, SB2_DGPU_POWER_OFF);
- +err_set_power:
- + platform_set_drvdata(pdev, NULL);
- + kfree(drvdata);
- +err_alloc_drvdata:
- + acpi_dev_remove_driver_gpios(shps_dev);
- + return status;
- +}
- +
- +static int sb2_shps_remove(struct platform_device *pdev)
- +{
- + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
- + struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev);
- +
- + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr);
- +
- + if (param_dgpu_power_exit != SB2_PARAM_DGPU_POWER_AS_IS) {
- + sb2_shps_dgpu_set_power(pdev, param_dgpu_power_exit);
- + }
- + acpi_dev_remove_driver_gpios(shps_dev);
- +
- + platform_set_drvdata(pdev, NULL);
- + kfree(drvdata);
- +
- + return 0;
- +}
- +
- +
- +static const struct acpi_device_id sb2_shps_acpi_match[] = {
- + { "MSHW0153", 0 },
- + { },
- +};
- +MODULE_DEVICE_TABLE(acpi, sb2_shps_acpi_match);
- +
- +static struct platform_driver sb2_shps_driver = {
- + .probe = sb2_shps_probe,
- + .remove = sb2_shps_remove,
- + .driver = {
- + .name = "sb2_shps",
- + .acpi_match_table = ACPI_PTR(sb2_shps_acpi_match),
- +#ifdef CONFIG_PM
- + .pm = &sb2_shps_pm_ops,
- +#endif
- + },
- +};
- +module_platform_driver(sb2_shps_driver);
- +
- +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
- +MODULE_DESCRIPTION("Surface Book 2 Hot-Plug System Driver");
- +MODULE_LICENSE("GPL v2");
- --
- 2.22.0
|