1 // SPDX-License-Identifier: GPL-2.0-only
3 * Driver for the LID cover switch of the Surface 3
5 * Copyright (c) 2016 Red Hat Inc.
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/slab.h>
13 #include <linux/acpi.h>
14 #include <linux/dmi.h>
15 #include <linux/input.h>
16 #include <linux/mutex.h>
17 #include <linux/platform_device.h>
18 #include <linux/spi/spi.h>
20 MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>");
21 MODULE_DESCRIPTION("Surface 3 platform driver");
22 MODULE_LICENSE("GPL");
24 #define ACPI_BUTTON_HID_LID "PNP0C0D"
25 #define SPI_CTL_OBJ_NAME "SPI"
26 #define SPI_TS_OBJ_NAME "NTRG"
28 #define SURFACE3_LID_GUID "F7CC25EC-D20B-404C-8903-0ED4359C18AE"
30 MODULE_ALIAS("wmi:" SURFACE3_LID_GUID
);
32 static const struct dmi_system_id surface3_dmi_table
[] = {
33 #if defined(CONFIG_X86)
36 DMI_MATCH(DMI_SYS_VENDOR
, "Microsoft Corporation"),
37 DMI_MATCH(DMI_PRODUCT_NAME
, "Surface 3"),
45 struct acpi_device
*touchscreen_adev
;
46 struct acpi_device
*pnp0c0d_adev
;
47 struct acpi_hotplug_context hp
;
48 struct input_dev
*input
;
51 static struct platform_device
*s3_wmi_pdev
;
53 static struct surface3_wmi s3_wmi
;
55 static DEFINE_MUTEX(s3_wmi_lock
);
57 static int s3_wmi_query_block(const char *guid
, int instance
, int *ret
)
59 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
60 union acpi_object
*obj
= NULL
;
64 mutex_lock(&s3_wmi_lock
);
65 status
= wmi_query_block(guid
, instance
, &output
);
66 if (ACPI_FAILURE(status
)) {
73 if (!obj
|| obj
->type
!= ACPI_TYPE_INTEGER
) {
75 pr_err("query block returned object type: %d - buffer length:%d\n",
77 obj
->type
== ACPI_TYPE_BUFFER
?
78 obj
->buffer
.length
: 0);
83 *ret
= obj
->integer
.value
;
86 mutex_unlock(&s3_wmi_lock
);
90 static inline int s3_wmi_query_lid(int *ret
)
92 return s3_wmi_query_block(SURFACE3_LID_GUID
, 0, ret
);
95 static int s3_wmi_send_lid_state(void)
99 ret
= s3_wmi_query_lid(&lid_sw
);
103 input_report_switch(s3_wmi
.input
, SW_LID
, lid_sw
);
104 input_sync(s3_wmi
.input
);
109 static int s3_wmi_hp_notify(struct acpi_device
*adev
, u32 value
)
111 return s3_wmi_send_lid_state();
114 static acpi_status
s3_wmi_attach_spi_device(acpi_handle handle
,
119 struct acpi_device
*adev
= acpi_fetch_acpi_dev(handle
);
120 struct acpi_device
**ts_adev
= data
;
122 if (!adev
|| strncmp(acpi_device_bid(adev
), SPI_TS_OBJ_NAME
,
123 strlen(SPI_TS_OBJ_NAME
)))
127 pr_err("duplicate entry %s\n", SPI_TS_OBJ_NAME
);
136 static int s3_wmi_check_platform_device(struct device
*dev
, void *data
)
138 struct acpi_device
*adev
= ACPI_COMPANION(dev
);
139 struct acpi_device
*ts_adev
= NULL
;
142 /* ignore non ACPI devices */
146 /* check for LID ACPI switch */
147 if (!strcmp(ACPI_BUTTON_HID_LID
, acpi_device_hid(adev
))) {
148 s3_wmi
.pnp0c0d_adev
= adev
;
152 /* ignore non SPI controllers */
153 if (strncmp(acpi_device_bid(adev
), SPI_CTL_OBJ_NAME
,
154 strlen(SPI_CTL_OBJ_NAME
)))
157 status
= acpi_walk_namespace(ACPI_TYPE_DEVICE
, adev
->handle
, 1,
158 s3_wmi_attach_spi_device
, NULL
,
160 if (ACPI_FAILURE(status
))
161 dev_warn(dev
, "failed to enumerate SPI slaves\n");
166 s3_wmi
.touchscreen_adev
= ts_adev
;
171 static int s3_wmi_create_and_register_input(struct platform_device
*pdev
)
173 struct input_dev
*input
;
176 input
= devm_input_allocate_device(&pdev
->dev
);
180 input
->name
= "Lid Switch";
181 input
->phys
= "button/input0";
182 input
->id
.bustype
= BUS_HOST
;
183 input
->id
.product
= 0x0005;
185 input_set_capability(input
, EV_SW
, SW_LID
);
187 error
= input_register_device(input
);
191 s3_wmi
.input
= input
;
196 static int __init
s3_wmi_probe(struct platform_device
*pdev
)
200 if (!dmi_check_system(surface3_dmi_table
))
203 memset(&s3_wmi
, 0, sizeof(s3_wmi
));
205 bus_for_each_dev(&platform_bus_type
, NULL
, NULL
,
206 s3_wmi_check_platform_device
);
208 if (!s3_wmi
.touchscreen_adev
)
211 acpi_bus_trim(s3_wmi
.pnp0c0d_adev
);
213 error
= s3_wmi_create_and_register_input(pdev
);
215 goto restore_acpi_lid
;
217 acpi_initialize_hp_context(s3_wmi
.touchscreen_adev
, &s3_wmi
.hp
,
218 s3_wmi_hp_notify
, NULL
);
220 s3_wmi_send_lid_state();
225 acpi_bus_scan(s3_wmi
.pnp0c0d_adev
->handle
);
229 static void s3_wmi_remove(struct platform_device
*device
)
231 /* remove the hotplug context from the acpi device */
232 s3_wmi
.touchscreen_adev
->hp
= NULL
;
234 /* reinstall the actual PNPC0C0D LID default handle */
235 acpi_bus_scan(s3_wmi
.pnp0c0d_adev
->handle
);
238 static int __maybe_unused
s3_wmi_resume(struct device
*dev
)
240 s3_wmi_send_lid_state();
243 static SIMPLE_DEV_PM_OPS(s3_wmi_pm
, NULL
, s3_wmi_resume
);
245 static struct platform_driver s3_wmi_driver
= {
247 .name
= "surface3-wmi",
250 .remove
= s3_wmi_remove
,
253 static int __init
s3_wmi_init(void)
257 s3_wmi_pdev
= platform_device_alloc("surface3-wmi", -1);
261 error
= platform_device_add(s3_wmi_pdev
);
265 error
= platform_driver_probe(&s3_wmi_driver
, s3_wmi_probe
);
269 pr_info("Surface 3 WMI Extras loaded\n");
273 platform_device_del(s3_wmi_pdev
);
275 platform_device_put(s3_wmi_pdev
);
279 static void __exit
s3_wmi_exit(void)
281 platform_device_unregister(s3_wmi_pdev
);
282 platform_driver_unregister(&s3_wmi_driver
);
285 module_init(s3_wmi_init
);
286 module_exit(s3_wmi_exit
);