Add dGPU hot-plug/power-setting support

This commit is contained in:
qzed 2019-07-03 00:37:16 +02:00
parent 002a33b6f0
commit 2009a8d721
24 changed files with 740 additions and 22 deletions

View file

@ -1,7 +1,7 @@
From e9f55e5da497d27e0908cbffd3ecd0e8f1369ddb Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:43:58 +0200
Subject: [PATCH 01/11] surface-acpi
Subject: [PATCH 01/12] surface-acpi
---
drivers/acpi/acpica/dsopcode.c | 2 +-

View file

@ -1,7 +1,7 @@
From e851005607738247775e43e9ac4336edd4c09516 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:45:42 +0200
Subject: [PATCH 02/11] suspend
Subject: [PATCH 02/12] suspend
---
drivers/nvme/host/nvme.h | 5 +++++

View file

@ -1,7 +1,7 @@
From ba453a89148ea2289e71741225273a02ae2db768 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:49:56 +0200
Subject: [PATCH 03/11] buttons
Subject: [PATCH 03/12] buttons
---
drivers/input/misc/soc_button_array.c | 145 ++++++++++++++++++++--

View file

@ -1,7 +1,7 @@
From 4f79924d2848c13e9a0830c0c575421768f8d58a Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:50:29 +0200
Subject: [PATCH 04/11] cameras
Subject: [PATCH 04/12] cameras
---
drivers/media/usb/uvc/uvc_driver.c | 40 +

View file

@ -1,7 +1,7 @@
From aa2ce90ff8ee8ad90a5e84004b748e709167bd0f Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:50:47 +0200
Subject: [PATCH 05/11] ipts
Subject: [PATCH 05/12] ipts
---
drivers/gpu/drm/i915/Makefile | 3 +

View file

@ -1,7 +1,7 @@
From 3895e7b026d525d70fdc0f4a6dd59b08c9f44022 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:51:26 +0200
Subject: [PATCH 06/11] hid
Subject: [PATCH 06/12] hid
---
drivers/hid/hid-ids.h | 21 +++++++++----

View file

@ -1,7 +1,7 @@
From 4cbc99c208e1938349aec603aee10b202a54f54a Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:51:55 +0200
Subject: [PATCH 07/11] sdcard-reader
Subject: [PATCH 07/12] sdcard-reader
---
drivers/usb/core/hub.c | 3 ++-

View file

@ -1,7 +1,7 @@
From 52cb41ede0999511587b9a2523771238c12f8033 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:52:12 +0200
Subject: [PATCH 08/11] wifi
Subject: [PATCH 08/12] wifi
---
drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 3 +--

View file

@ -1,7 +1,7 @@
From 09ddddeb396321184eb2591c20e681d48623c34d Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:52:29 +0200
Subject: [PATCH 09/11] surface3-power
Subject: [PATCH 09/12] surface3-power
---
drivers/platform/x86/Kconfig | 7 +

View file

@ -1,7 +1,7 @@
From d27de602a55f146e79c125287e6dbc3aa70ded38 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:52:52 +0200
Subject: [PATCH 10/11] mwlwifi
Subject: [PATCH 10/12] mwlwifi
---
drivers/net/wireless/marvell/Kconfig | 1 +

View file

@ -1,7 +1,7 @@
From 4b31faa2ddaffa6e6963c54ff6574178de94918e Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:53:15 +0200
Subject: [PATCH 11/11] surface-lte
Subject: [PATCH 11/12] surface-lte
---
drivers/usb/serial/qcserial.c | 1 +

View file

@ -0,0 +1,359 @@
From c7eb71b21227f0ee6b46a13c9b4f9df49dd23979 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Tue, 2 Jul 2019 23:58:22 +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 d6695d8fc795..16f40109337c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -436,6 +436,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 dd045ccf3a90..6d24ede71496 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -45,6 +45,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, &param, 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, &param_dgpu_power_ops, &param_dgpu_power_init, SB2_PARAM_PERM);
+module_param_cb(dgpu_power_exit, &param_dgpu_power_ops, &param_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

View file

@ -1,7 +1,7 @@
From d4d2fa3647f86b84ed6d38c8180cb020eb88627d Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:22:49 +0200
Subject: [PATCH 01/11] surface-acpi
Subject: [PATCH 01/12] surface-acpi
---
drivers/acpi/acpica/dsopcode.c | 2 +-

View file

@ -1,7 +1,7 @@
From c2ae9c80d630190f67b102303f794277cd916d9f Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:24:31 +0200
Subject: [PATCH 02/11] suspend
Subject: [PATCH 02/12] suspend
---
drivers/nvme/host/nvme.h | 5 +++++

View file

@ -1,7 +1,7 @@
From 8b583fd83792f28f23461186b0c44d9c1554204e Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:25:27 +0200
Subject: [PATCH 03/11] buttons
Subject: [PATCH 03/12] buttons
---
drivers/input/misc/soc_button_array.c | 145 ++++++++++++++++++++--

View file

@ -1,7 +1,7 @@
From ee99ccaced33d4db9d807b5452507c2b4257f595 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:27:27 +0200
Subject: [PATCH 04/11] cameras
Subject: [PATCH 04/12] cameras
---
drivers/media/usb/uvc/uvc_driver.c | 40 +

View file

@ -1,7 +1,7 @@
From fa6be7e31d29959edc738986507fed1e1a877e2e Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:28:10 +0200
Subject: [PATCH 05/11] ipts
Subject: [PATCH 05/12] ipts
---
drivers/gpu/drm/i915/Makefile | 3 +

View file

@ -1,7 +1,7 @@
From d784fa6928c75c033a240d6db839c3aacd9edd33 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:28:41 +0200
Subject: [PATCH 06/11] hid
Subject: [PATCH 06/12] hid
---
drivers/hid/hid-ids.h | 21 +++++++++----

View file

@ -1,7 +1,7 @@
From 1b434e8d2943d1c6a3fb9eb7936f8ecc5c1551e3 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:29:19 +0200
Subject: [PATCH 07/11] sdcard-reader
Subject: [PATCH 07/12] sdcard-reader
---
drivers/usb/core/hub.c | 3 ++-

View file

@ -1,7 +1,7 @@
From 8f2bb111745b57988030776899049153dadf2e3d Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:30:18 +0200
Subject: [PATCH 08/11] wifi
Subject: [PATCH 08/12] wifi
---
drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 3 +--

View file

@ -1,7 +1,7 @@
From facb57708454c1424e5dffe2be999356fc88984a Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:30:44 +0200
Subject: [PATCH 09/11] surface3-power
Subject: [PATCH 09/12] surface3-power
---
drivers/platform/x86/Kconfig | 7 +

View file

@ -1,7 +1,7 @@
From 74d3a35c573f41861590ee786400067220c300b6 Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:31:18 +0200
Subject: [PATCH 10/11] mwlwifi
Subject: [PATCH 10/12] mwlwifi
---
drivers/net/wireless/marvell/Kconfig | 1 +

View file

@ -1,7 +1,7 @@
From ba5ab6c097f826ef6ec92379383f8b9963b8f48c Mon Sep 17 00:00:00 2001
From: Maximilian Luz <luzmaximilian@gmail.com>
Date: Wed, 3 Jul 2019 00:32:00 +0200
Subject: [PATCH 11/11] surface-lte
Subject: [PATCH 11/12] surface-lte
---
drivers/usb/serial/qcserial.c | 1 +

View file

@ -0,0 +1,359 @@
From 42b801e957cc7aa47e7d6a869970c149d959b539 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, &param, 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, &param_dgpu_power_ops, &param_dgpu_power_init, SB2_PARAM_PERM);
+module_param_cb(dgpu_power_exit, &param_dgpu_power_ops, &param_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