2 * Driver for the LID cover switch of the Surface 3
4 * Copyright (c) 2016 Red Hat Inc.
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; version 2 of the License.
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
17 #include <linux/acpi.h>
18 #include <linux/dmi.h>
19 #include <linux/input.h>
20 #include <linux/mutex.h>
21 #include <linux/platform_device.h>
22 #include <linux/spi/spi.h>
24 MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>");
25 MODULE_DESCRIPTION("Surface 3 platform driver");
26 MODULE_LICENSE("GPL");
28 #define ACPI_BUTTON_HID_LID "PNP0C0D"
29 #define SPI_CTL_OBJ_NAME "SPI"
30 #define SPI_TS_OBJ_NAME "NTRG"
32 #define SURFACE3_LID_GUID "F7CC25EC-D20B-404C-8903-0ED4359C18AE"
34 MODULE_ALIAS("wmi:" SURFACE3_LID_GUID
);
36 static const struct dmi_system_id surface3_dmi_table
[] = {
37 #if defined(CONFIG_X86)
40 DMI_MATCH(DMI_SYS_VENDOR
, "Microsoft Corporation"),
41 DMI_MATCH(DMI_PRODUCT_NAME
, "Surface 3"),
49 struct acpi_device
*touchscreen_adev
;
50 struct acpi_device
*pnp0c0d_adev
;
51 struct acpi_hotplug_context hp
;
52 struct input_dev
*input
;
55 static struct platform_device
*s3_wmi_pdev
;
57 static struct surface3_wmi s3_wmi
;
59 static DEFINE_MUTEX(s3_wmi_lock
);
61 static int s3_wmi_query_block(const char *guid
, int instance
, int *ret
)
63 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
65 union acpi_object
*obj
;
68 mutex_lock(&s3_wmi_lock
);
69 status
= wmi_query_block(guid
, instance
, &output
);
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
, **ts_adev
;
121 if (acpi_bus_get_device(handle
, &adev
))
126 if (strncmp(acpi_device_bid(adev
), SPI_TS_OBJ_NAME
,
127 strlen(SPI_TS_OBJ_NAME
)))
131 pr_err("duplicate entry %s\n", SPI_TS_OBJ_NAME
);
140 static int s3_wmi_check_platform_device(struct device
*dev
, void *data
)
142 struct acpi_device
*adev
, *ts_adev
= NULL
;
146 /* ignore non ACPI devices */
147 handle
= ACPI_HANDLE(dev
);
148 if (!handle
|| acpi_bus_get_device(handle
, &adev
))
151 /* check for LID ACPI switch */
152 if (!strcmp(ACPI_BUTTON_HID_LID
, acpi_device_hid(adev
))) {
153 s3_wmi
.pnp0c0d_adev
= adev
;
157 /* ignore non SPI controllers */
158 if (strncmp(acpi_device_bid(adev
), SPI_CTL_OBJ_NAME
,
159 strlen(SPI_CTL_OBJ_NAME
)))
162 status
= acpi_walk_namespace(ACPI_TYPE_DEVICE
, handle
, 1,
163 s3_wmi_attach_spi_device
, NULL
,
165 if (ACPI_FAILURE(status
))
166 dev_warn(dev
, "failed to enumerate SPI slaves\n");
171 s3_wmi
.touchscreen_adev
= ts_adev
;
176 static int s3_wmi_create_and_register_input(struct platform_device
*pdev
)
178 struct input_dev
*input
;
181 input
= devm_input_allocate_device(&pdev
->dev
);
185 input
->name
= "Lid Switch";
186 input
->phys
= "button/input0";
187 input
->id
.bustype
= BUS_HOST
;
188 input
->id
.product
= 0x0005;
190 input_set_capability(input
, EV_SW
, SW_LID
);
192 error
= input_register_device(input
);
196 s3_wmi
.input
= input
;
200 input_free_device(s3_wmi
.input
);
204 static int __init
s3_wmi_probe(struct platform_device
*pdev
)
208 if (!dmi_check_system(surface3_dmi_table
))
211 memset(&s3_wmi
, 0, sizeof(s3_wmi
));
213 bus_for_each_dev(&platform_bus_type
, NULL
, NULL
,
214 s3_wmi_check_platform_device
);
216 if (!s3_wmi
.touchscreen_adev
)
219 acpi_bus_trim(s3_wmi
.pnp0c0d_adev
);
221 error
= s3_wmi_create_and_register_input(pdev
);
223 goto restore_acpi_lid
;
225 acpi_initialize_hp_context(s3_wmi
.touchscreen_adev
, &s3_wmi
.hp
,
226 s3_wmi_hp_notify
, NULL
);
228 s3_wmi_send_lid_state();
233 acpi_bus_scan(s3_wmi
.pnp0c0d_adev
->handle
);
237 static int s3_wmi_remove(struct platform_device
*device
)
239 /* remove the hotplug context from the acpi device */
240 s3_wmi
.touchscreen_adev
->hp
= NULL
;
242 /* reinstall the actual PNPC0C0D LID default handle */
243 acpi_bus_scan(s3_wmi
.pnp0c0d_adev
->handle
);
247 static int __maybe_unused
s3_wmi_resume(struct device
*dev
)
249 s3_wmi_send_lid_state();
252 static SIMPLE_DEV_PM_OPS(s3_wmi_pm
, NULL
, s3_wmi_resume
);
254 static struct platform_driver s3_wmi_driver
= {
256 .name
= "surface3-wmi",
259 .remove
= s3_wmi_remove
,
262 static int __init
s3_wmi_init(void)
266 s3_wmi_pdev
= platform_device_alloc("surface3-wmi", -1);
270 error
= platform_device_add(s3_wmi_pdev
);
274 error
= platform_driver_probe(&s3_wmi_driver
, s3_wmi_probe
);
278 pr_info("Surface 3 WMI Extras loaded\n");
282 platform_device_del(s3_wmi_pdev
);
284 platform_device_put(s3_wmi_pdev
);
288 static void __exit
s3_wmi_exit(void)
290 platform_device_unregister(s3_wmi_pdev
);
291 platform_driver_unregister(&s3_wmi_driver
);
294 module_init(s3_wmi_init
);
295 module_exit(s3_wmi_exit
);