0012-surfacebook2-dgpu.patch 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. From ff7a7f5931f8f0aae029daa36209686818aafbe1 Mon Sep 17 00:00:00 2001
  2. From: Maximilian Luz <luzmaximilian@gmail.com>
  3. Date: Wed, 3 Jul 2019 00:35:24 +0200
  4. Subject: [PATCH 12/12] surfacebook2-dgpu
  5. ---
  6. drivers/platform/x86/Kconfig | 9 +
  7. drivers/platform/x86/Makefile | 1 +
  8. drivers/platform/x86/surfacebook2_dgpu_hps.c | 306 +++++++++++++++++++
  9. 3 files changed, 316 insertions(+)
  10. create mode 100644 drivers/platform/x86/surfacebook2_dgpu_hps.c
  11. diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
  12. index 21ffc03cd372..200d4d55da98 100644
  13. --- a/drivers/platform/x86/Kconfig
  14. +++ b/drivers/platform/x86/Kconfig
  15. @@ -483,6 +483,15 @@ config SURFACE3_WMI
  16. To compile this driver as a module, choose M here: the module will
  17. be called surface3-wmi.
  18. +config SURFACE_BOOK2_DGPU_HPS
  19. + tristate "Surface Book 2 dGPU Hot-Plug System Driver"
  20. + depends on ACPI
  21. + ---help---
  22. + This is an experimetnal driver to control the power-state of the
  23. + Surface Book 2 dGPU.
  24. +
  25. + If you have a Surface Book 2, say Y or M here.
  26. +
  27. config THINKPAD_ACPI
  28. tristate "ThinkPad ACPI Laptop Extras"
  29. depends on ACPI
  30. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
  31. index 278ce32df99f..0faf98020b84 100644
  32. --- a/drivers/platform/x86/Makefile
  33. +++ b/drivers/platform/x86/Makefile
  34. @@ -49,6 +49,7 @@ obj-$(CONFIG_ACPI_WMI) += wmi.o
  35. obj-$(CONFIG_MSI_WMI) += msi-wmi.o
  36. obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
  37. obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
  38. +obj-$(CONFIG_SURFACE_BOOK2_DGPU_HPS) += surfacebook2_dgpu_hps.o
  39. obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
  40. obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
  41. obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
  42. diff --git a/drivers/platform/x86/surfacebook2_dgpu_hps.c b/drivers/platform/x86/surfacebook2_dgpu_hps.c
  43. new file mode 100644
  44. index 000000000000..7639fb0029d8
  45. --- /dev/null
  46. +++ b/drivers/platform/x86/surfacebook2_dgpu_hps.c
  47. @@ -0,0 +1,306 @@
  48. +#include <linux/acpi.h>
  49. +#include <linux/gpio.h>
  50. +#include <linux/kernel.h>
  51. +#include <linux/module.h>
  52. +#include <linux/moduleparam.h>
  53. +#include <linux/platform_device.h>
  54. +#include <linux/sysfs.h>
  55. +
  56. +#include <linux/uaccess.h>
  57. +
  58. +
  59. +#define SB2_SHPS_DSM_REVISION 1
  60. +#define SB2_SHPS_DSM_GPU_STATE 0x05
  61. +
  62. +static const guid_t SB2_SHPS_DSM_UUID =
  63. + GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd,
  64. + 0x32, 0x0e, 0x10, 0x36, 0x0a);
  65. +
  66. +#define SB2_PARAM_PERM (S_IRUGO | S_IWUSR)
  67. +
  68. +
  69. +static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false };
  70. +static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false };
  71. +static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false };
  72. +static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false };
  73. +static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false };
  74. +static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false };
  75. +
  76. +static const struct acpi_gpio_mapping sb2_mshw0153_acpi_gpios[] = {
  77. + { "base_presence-int-gpio", &gpio_base_presence_int, 1 },
  78. + { "base_presence-gpio", &gpio_base_presence, 1 },
  79. + { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 },
  80. + { "dgpu_power-gpio", &gpio_dgpu_power, 1 },
  81. + { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 },
  82. + { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 },
  83. + { },
  84. +};
  85. +
  86. +
  87. +enum sb2_dgpu_power {
  88. + SB2_DGPU_POWER_OFF = 0,
  89. + SB2_DGPU_POWER_ON = 1,
  90. +
  91. + __SB2_DGPU_POWER__START = 0,
  92. + __SB2_DGPU_POWER__END = 1,
  93. +};
  94. +
  95. +enum sb2_param_dgpu_power {
  96. + SB2_PARAM_DGPU_POWER_OFF = SB2_DGPU_POWER_OFF,
  97. + SB2_PARAM_DGPU_POWER_ON = SB2_DGPU_POWER_ON,
  98. + SB2_PARAM_DGPU_POWER_AS_IS = 2,
  99. +
  100. + __SB2_PARAM_DGPU_POWER__START = 0,
  101. + __SB2_PARAM_DGPU_POWER__END = 2,
  102. +};
  103. +
  104. +static const char* sb2_dgpu_power_str(enum sb2_dgpu_power power) {
  105. + if (power == SB2_DGPU_POWER_OFF) {
  106. + return "off";
  107. + } else if (power == SB2_DGPU_POWER_ON) {
  108. + return "on";
  109. + } else {
  110. + return "<invalid>";
  111. + }
  112. +}
  113. +
  114. +
  115. +struct sb2_shps_driver_data {
  116. + struct mutex dgpu_power_lock;
  117. + enum sb2_dgpu_power dgpu_power;
  118. +};
  119. +
  120. +
  121. +static int __sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power)
  122. +{
  123. + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
  124. + acpi_handle handle = ACPI_HANDLE(&pdev->dev);
  125. + union acpi_object *result;
  126. + union acpi_object param;
  127. +
  128. + param.type = ACPI_TYPE_INTEGER;
  129. + param.integer.value = power == SB2_DGPU_POWER_ON;
  130. +
  131. + result = acpi_evaluate_dsm_typed(handle, &SB2_SHPS_DSM_UUID, SB2_SHPS_DSM_REVISION,
  132. + SB2_SHPS_DSM_GPU_STATE, &param, ACPI_TYPE_BUFFER);
  133. +
  134. + if (IS_ERR_OR_NULL(result)) {
  135. + return result ? PTR_ERR(result) : -EFAULT;
  136. + }
  137. +
  138. + if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) {
  139. + return -EIO;
  140. + }
  141. +
  142. + drvdata->dgpu_power = power;
  143. +
  144. + printk(KERN_INFO "sb2_shps: dGPU power state set to \'%s\'\n", sb2_dgpu_power_str(power));
  145. +
  146. + ACPI_FREE(result);
  147. + return 0;
  148. +}
  149. +
  150. +static int sb2_shps_dgpu_set_power(struct platform_device *pdev, enum sb2_dgpu_power power)
  151. +{
  152. + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
  153. + int status = 0;
  154. +
  155. + if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) {
  156. + return -EINVAL;
  157. + }
  158. +
  159. + mutex_lock(&drvdata->dgpu_power_lock);
  160. + if (power != drvdata->dgpu_power) {
  161. + status = __sb2_shps_dgpu_set_power(pdev, power);
  162. + }
  163. + mutex_unlock(&drvdata->dgpu_power_lock);
  164. +
  165. + return status;
  166. +}
  167. +
  168. +static int sb2_shps_dgpu_force_power(struct platform_device *pdev, enum sb2_dgpu_power power)
  169. +{
  170. + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
  171. + int status;
  172. +
  173. + if (power < __SB2_DGPU_POWER__START || power > __SB2_DGPU_POWER__END) {
  174. + return -EINVAL;
  175. + }
  176. +
  177. + mutex_lock(&drvdata->dgpu_power_lock);
  178. + status = __sb2_shps_dgpu_set_power(pdev, power);
  179. + mutex_unlock(&drvdata->dgpu_power_lock);
  180. +
  181. + return status;
  182. +}
  183. +
  184. +
  185. +static int param_dgpu_power_set(const char *val, const struct kernel_param *kp)
  186. +{
  187. + int power = SB2_PARAM_DGPU_POWER_OFF;
  188. + int status;
  189. +
  190. + status = kstrtoint(val, 0, &power);
  191. + if (status) {
  192. + return status;
  193. + }
  194. +
  195. + if (power < __SB2_PARAM_DGPU_POWER__START || power > __SB2_PARAM_DGPU_POWER__END) {
  196. + return -EINVAL;
  197. + }
  198. +
  199. + return param_set_int(val, kp);
  200. +}
  201. +
  202. +static const struct kernel_param_ops param_dgpu_power_ops = {
  203. + .set = param_dgpu_power_set,
  204. + .get = param_get_int,
  205. +};
  206. +
  207. +static int param_dgpu_power_init = SB2_PARAM_DGPU_POWER_OFF;
  208. +static int param_dgpu_power_exit = SB2_PARAM_DGPU_POWER_OFF;
  209. +
  210. +module_param_cb(dgpu_power_init, &param_dgpu_power_ops, &param_dgpu_power_init, SB2_PARAM_PERM);
  211. +module_param_cb(dgpu_power_exit, &param_dgpu_power_ops, &param_dgpu_power_exit, SB2_PARAM_PERM);
  212. +
  213. +MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is)");
  214. +MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is)");
  215. +
  216. +
  217. +static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data)
  218. +{
  219. + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
  220. + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
  221. +
  222. + return sprintf(data, "%s\n", sb2_dgpu_power_str(drvdata->dgpu_power));
  223. +}
  224. +
  225. +static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr,
  226. + const char *data, size_t count)
  227. +{
  228. + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
  229. + bool power = false;
  230. + int status;
  231. +
  232. + status = kstrtobool(data, &power);
  233. + if (status) {
  234. + return status;
  235. + }
  236. +
  237. + if (power) {
  238. + status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_ON);
  239. + } else {
  240. + status = sb2_shps_dgpu_set_power(pdev, SB2_DGPU_POWER_OFF);
  241. + }
  242. +
  243. + return status < 0 ? status : count;
  244. +}
  245. +
  246. +const static DEVICE_ATTR_RW(dgpu_power);
  247. +
  248. +
  249. +#ifdef CONFIG_PM
  250. +
  251. +static int sb2_shps_resume(struct device *dev)
  252. +{
  253. + struct platform_device *pdev = container_of(dev, struct platform_device, dev);
  254. + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
  255. +
  256. + return sb2_shps_dgpu_force_power(pdev, drvdata->dgpu_power);
  257. +}
  258. +
  259. +static SIMPLE_DEV_PM_OPS(sb2_shps_pm_ops, NULL, sb2_shps_resume);
  260. +
  261. +#endif
  262. +
  263. +
  264. +static int sb2_shps_probe(struct platform_device *pdev)
  265. +{
  266. + struct sb2_shps_driver_data *drvdata;
  267. + struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev);
  268. + int status = 0;
  269. +
  270. + if (gpiod_count(&pdev->dev, NULL) < 0) {
  271. + return -ENODEV;
  272. + }
  273. +
  274. + status = acpi_dev_add_driver_gpios(shps_dev, sb2_mshw0153_acpi_gpios);
  275. + if (status) {
  276. + return status;
  277. + }
  278. +
  279. + drvdata = kzalloc(sizeof(struct sb2_shps_driver_data), GFP_KERNEL);
  280. + if (!drvdata) {
  281. + status = -ENOMEM;
  282. + goto err_alloc_drvdata;
  283. + }
  284. +
  285. + mutex_init(&drvdata->dgpu_power_lock);
  286. + drvdata->dgpu_power = SB2_DGPU_POWER_OFF;
  287. + platform_set_drvdata(pdev, drvdata);
  288. +
  289. + if (param_dgpu_power_init != SB2_PARAM_DGPU_POWER_AS_IS) {
  290. + status = sb2_shps_dgpu_force_power(pdev, param_dgpu_power_init);
  291. + if (status) {
  292. + goto err_set_power;
  293. + }
  294. + }
  295. +
  296. + status = sysfs_create_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr);
  297. + if (status) {
  298. + goto err_sysfs;
  299. + }
  300. +
  301. + return 0;
  302. +
  303. +err_sysfs:
  304. + sb2_shps_dgpu_force_power(pdev, SB2_DGPU_POWER_OFF);
  305. +err_set_power:
  306. + platform_set_drvdata(pdev, NULL);
  307. + kfree(drvdata);
  308. +err_alloc_drvdata:
  309. + acpi_dev_remove_driver_gpios(shps_dev);
  310. + return status;
  311. +}
  312. +
  313. +static int sb2_shps_remove(struct platform_device *pdev)
  314. +{
  315. + struct sb2_shps_driver_data *drvdata = platform_get_drvdata(pdev);
  316. + struct acpi_device *shps_dev = ACPI_COMPANION(&pdev->dev);
  317. +
  318. + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_dgpu_power.attr);
  319. +
  320. + if (param_dgpu_power_exit != SB2_PARAM_DGPU_POWER_AS_IS) {
  321. + sb2_shps_dgpu_set_power(pdev, param_dgpu_power_exit);
  322. + }
  323. + acpi_dev_remove_driver_gpios(shps_dev);
  324. +
  325. + platform_set_drvdata(pdev, NULL);
  326. + kfree(drvdata);
  327. +
  328. + return 0;
  329. +}
  330. +
  331. +
  332. +static const struct acpi_device_id sb2_shps_acpi_match[] = {
  333. + { "MSHW0153", 0 },
  334. + { },
  335. +};
  336. +MODULE_DEVICE_TABLE(acpi, sb2_shps_acpi_match);
  337. +
  338. +static struct platform_driver sb2_shps_driver = {
  339. + .probe = sb2_shps_probe,
  340. + .remove = sb2_shps_remove,
  341. + .driver = {
  342. + .name = "sb2_shps",
  343. + .acpi_match_table = ACPI_PTR(sb2_shps_acpi_match),
  344. +#ifdef CONFIG_PM
  345. + .pm = &sb2_shps_pm_ops,
  346. +#endif
  347. + },
  348. +};
  349. +module_platform_driver(sb2_shps_driver);
  350. +
  351. +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
  352. +MODULE_DESCRIPTION("Surface Book 2 Hot-Plug System Driver");
  353. +MODULE_LICENSE("GPL v2");
  354. --
  355. 2.22.0