This repository has been archived by the owner on Dec 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy path0007-Add-IdeaPad-Usage-Mode-driver.patch
338 lines (332 loc) · 8.97 KB
/
0007-Add-IdeaPad-Usage-Mode-driver.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
From eb9e6a8c9a65fd7b47034722ca07f39c7487e8f9 Mon Sep 17 00:00:00 2001
From: Philipp Jungkamp <[email protected]>
Date: Sat, 30 Jul 2022 15:46:13 +0200
Subject: [PATCH 7/8] Add IdeaPad Usage Mode driver
The so called 'usage mode' reported by the firmware of the Lenovo Yoga
line via ACPI WMI is mapped to a tablet mode switch input device.
Signed-off-by: Philipp Jungkamp <[email protected]>
---
drivers/platform/x86/Kconfig | 11 +
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/ideapad-wmi-usage-mode.c | 279 ++++++++++++++++++
3 files changed, 291 insertions(+)
create mode 100644 drivers/platform/x86/ideapad-wmi-usage-mode.c
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index f2f98e942cf2..3e4f9ff4d7b7 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -140,6 +140,17 @@ config YOGABOOK_WMI
To compile this driver as a module, choose M here: the module will
be called lenovo-yogabook-wmi.
+config IDEAPAD_WMI_USAGE_MODE
+ tristate "Lenovo IdeaPad WMI usage mode"
+ depends on ACPI_WMI
+ depends on INPUT
+ help
+ Say Y here if you want to receive SW_TABLET_MODE events depending on
+ the usage mode (Laptop/Tent/Tablet/Drawboard) of the device.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ideapad-wmi-usage-mode.
+
config ACERHDF
tristate "Acer Aspire One temperature and fan driver"
depends on ACPI && THERMAL
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 5a428caa654a..a9f5009e4a3b 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o
+obj-$(CONFIG_IDEAPAD_WMI_USAGE_MODE) += ideapad-wmi-usage-mode.o
# Acer
obj-$(CONFIG_ACERHDF) += acerhdf.o
diff --git a/drivers/platform/x86/ideapad-wmi-usage-mode.c b/drivers/platform/x86/ideapad-wmi-usage-mode.c
new file mode 100644
index 000000000000..23d090c34d61
--- /dev/null
+++ b/drivers/platform/x86/ideapad-wmi-usage-mode.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Ideapad WMI usage mode driver
+ *
+ * Maps the usage mode reported by the Lenovo Yoga firmware to a
+ * tablet mode switch.
+ *
+ * Copyright (C) 2022 Philipp Jungkamp <[email protected]>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/wmi.h>
+
+#define IDEAPAD_USAGE_MODE_EVENT_GUID "06129D99-6083-4164-81AD-F092F9D773A6"
+#define IDEAPAD_USAGE_MODE_QUERY_GUID "09B0EE6E-C3FD-4243-8DA1-7911FF80BB8C"
+
+enum ideapad_wmi_type {
+ IDEAPAD_WMI_USAGE_MODE_EVENT,
+ IDEAPAD_WMI_USAGE_MODE_QUERY,
+};
+
+struct ideapad_wmi_shared {
+ struct mutex *mutex;
+ struct wmi_device *event;
+ struct wmi_device *query;
+ struct input_dev *input_dev;
+};
+
+struct ideapad_wmi_private {
+ enum ideapad_wmi_type type;
+ struct ideapad_wmi_shared *shared;
+};
+
+static const struct key_entry ideapad_wmi_usage_mode_keymap[] = {
+ /* Laptop usage mode */
+ { KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } },
+ /* Tablet usage mode */
+ { KE_SW, 0x02, { .sw = { SW_TABLET_MODE, 1 } } },
+ /* Drawing Board usage mode */
+ { KE_SW, 0x03, { .sw = { SW_TABLET_MODE, 1 } } },
+ /* Tent usage mode */
+ { KE_SW, 0x04, { .sw = { SW_TABLET_MODE, 1 } } },
+ { KE_END },
+};
+
+static void ideapad_wmi_input_report(struct ideapad_wmi_private *priv,
+ unsigned int scancode)
+{
+ dev_dbg(&priv->shared->query->dev,
+ "WMI report scancode: %d\n", scancode);
+ sparse_keymap_report_event(priv->shared->input_dev, scancode, 0 /* ignored */, true);
+}
+
+static void ideapad_wmi_update_usage_mode(struct ideapad_wmi_private *priv)
+{
+ struct wmi_device *query = priv->shared->query;
+ union acpi_object *obj;
+ struct acpi_buffer input;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ int ret;
+
+ input.length = (acpi_size) 0;
+ input.pointer = NULL;
+
+ if (!query)
+ return;
+
+ ret = wmidev_evaluate_method(query, 0, 0, &input, &output);
+ if (ret) {
+ dev_warn(&query->dev,
+ "WMI query usage mode failed\n");
+ return;
+ }
+
+ obj = (union acpi_object *)output.pointer;
+
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ ideapad_wmi_input_report(priv, obj->integer.value);
+ else
+ dev_warn(&query->dev,
+ "WMI query usage mode is not an integer\n");
+
+ kfree(output.pointer);
+}
+
+static int ideapad_wmi_shared_init(struct ideapad_wmi_private *priv,
+ struct wmi_device *dev)
+{
+ struct ideapad_wmi_shared *shared = priv->shared;
+ struct input_dev *input_dev;
+ int err;
+
+ mutex_lock(shared->mutex);
+
+ switch (priv->type) {
+ case IDEAPAD_WMI_USAGE_MODE_EVENT:
+ shared->event = dev;
+ break;
+ case IDEAPAD_WMI_USAGE_MODE_QUERY:
+ shared->query = dev;
+ break;
+ }
+
+ if (!shared->event || !shared->query)
+ goto unlock;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ goto err_unlock;
+ }
+
+ input_dev->name = "IdeaPad WMI tablet mode switch";
+ input_dev->phys = IDEAPAD_USAGE_MODE_EVENT_GUID "/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &shared->event->dev;
+
+ err = sparse_keymap_setup(input_dev, ideapad_wmi_usage_mode_keymap, NULL);
+ if (err) {
+ dev_err(&shared->event->dev,
+ "Could not set up input device keymap: %d\n", err);
+ goto err_free_dev;
+ }
+
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&shared->event->dev,
+ "Could not register input device: %d\n", err);
+ goto err_free_dev;
+ }
+
+ shared->input_dev = input_dev;
+
+ ideapad_wmi_update_usage_mode(priv);
+unlock:
+ mutex_unlock(shared->mutex);
+ return 0;
+
+err_free_dev:
+ input_free_device(input_dev);
+err_unlock:
+ mutex_unlock(shared->mutex);
+ return err;
+}
+
+static void ideapad_wmi_shared_exit(struct ideapad_wmi_private *priv)
+{
+ struct ideapad_wmi_shared *shared = priv->shared;
+
+ mutex_lock(shared->mutex);
+
+ switch (priv->type) {
+ case IDEAPAD_WMI_USAGE_MODE_EVENT:
+ shared->event = NULL;
+ break;
+ case IDEAPAD_WMI_USAGE_MODE_QUERY:
+ shared->query = NULL;
+ break;
+ }
+
+ if (!shared->query && !shared->event) {
+ input_unregister_device(shared->input_dev);
+ shared->input_dev = NULL;
+ }
+
+ mutex_unlock(shared->mutex);
+}
+
+static int ideapad_wmi_probe(struct wmi_device *wdev, const void *ctx)
+{
+ const struct ideapad_wmi_private *context = ctx;
+ struct ideapad_wmi_private *priv;
+ int err;
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->type = context->type;
+ priv->shared = context->shared;
+
+ err = ideapad_wmi_shared_init(priv, wdev);
+ if (err)
+ return err;
+
+ dev_set_drvdata(&wdev->dev, priv);
+
+ return 0;
+}
+
+static void ideapad_wmi_remove(struct wmi_device *wdev)
+{
+ struct ideapad_wmi_private *priv = dev_get_drvdata(&wdev->dev);
+
+ ideapad_wmi_shared_exit(priv);
+}
+
+static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data)
+{
+ struct ideapad_wmi_private *priv = dev_get_drvdata(&wdev->dev);
+
+ dev_dbg(&priv->shared->query->dev,
+ "WMI notify\n");
+
+ ideapad_wmi_update_usage_mode(priv);
+}
+
+#ifdef CONFIG_PM_SLEEP
+#define ideapad_wmi_suspend NULL
+
+static int ideapad_wmi_resume(struct device *dev) {
+ struct ideapad_wmi_private *priv = dev_get_drvdata(dev);
+
+ ideapad_wmi_update_usage_mode(priv);
+
+ return 0;
+}
+
+#else
+#define ideapad_wmi_suspend NULL
+#define ideapad_wmi_resume NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(ideapad_wmi_pm, ideapad_wmi_suspend, ideapad_wmi_resume);
+
+static DEFINE_MUTEX(ideapad_wmi_shared_mutex);
+
+static struct ideapad_wmi_shared ideapad_wmi_shared = {
+ .mutex = &ideapad_wmi_shared_mutex,
+};
+
+static const struct ideapad_wmi_private ideapad_wmi_event_context = {
+ .type = IDEAPAD_WMI_USAGE_MODE_EVENT,
+ .shared = &ideapad_wmi_shared,
+};
+
+static const struct ideapad_wmi_private ideapad_wmi_query_context = {
+ .type = IDEAPAD_WMI_USAGE_MODE_QUERY,
+ .shared = &ideapad_wmi_shared,
+};
+
+static const struct wmi_device_id ideapad_wmi_id_table[] = {
+ { /* Usage mode change event */
+ .guid_string = IDEAPAD_USAGE_MODE_EVENT_GUID,
+ .context = &ideapad_wmi_event_context,
+ },
+ { /* Usage mode query method */
+ .guid_string = IDEAPAD_USAGE_MODE_QUERY_GUID,
+ .context = &ideapad_wmi_query_context,
+ },
+ { }
+};
+
+static struct wmi_driver ideapad_wmi_driver = {
+ .driver = {
+ .name = "ideapad-wmi-usage-mode",
+ .pm = &ideapad_wmi_pm,
+ },
+ .no_notify_data = true,
+ .id_table = ideapad_wmi_id_table,
+ .probe = ideapad_wmi_probe,
+ .remove = ideapad_wmi_remove,
+ .notify = ideapad_wmi_notify,
+};
+
+module_wmi_driver(ideapad_wmi_driver);
+
+MODULE_DEVICE_TABLE(wmi, ideapad_wmi_id_table);
+MODULE_AUTHOR("Philipp Jungkamp <[email protected]>");
+MODULE_DESCRIPTION("Ideapad WMI usage mode driver");
+MODULE_LICENSE("GPL");
--
2.38.1