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
};
61 union acpi_object
*obj
;
64 mutex_lock(&s3_wmi_lock
);
65 status
= wmi_query_block(guid
, instance
, &output
);
69 if (!obj
|| obj
->type
!= ACPI_TYPE_INTEGER
) {
71 pr_err("query block returned object type: %d - buffer length:%d\n",
73 obj
->type
== ACPI_TYPE_BUFFER
?
74 obj
->buffer
.length
: 0);
79 *ret
= obj
->integer
.value
;
82 mutex_unlock(&s3_wmi_lock
);
86 static inline int s3_wmi_query_lid(int *ret
)
88 return s3_wmi_query_block(SURFACE3_LID_GUID
, 0, ret
);
91 static int s3_wmi_send_lid_state(void)
95 ret
= s3_wmi_query_lid(&lid_sw
);
99 input_report_switch(s3_wmi
.input
, SW_LID
, lid_sw
);
100 input_sync(s3_wmi
.input
);
105 static int s3_wmi_hp_notify(struct acpi_device
*adev
, u32 value
)
107 return s3_wmi_send_lid_state();
110 static acpi_status
s3_wmi_attach_spi_device(acpi_handle handle
,
115 struct acpi_device
*adev
, **ts_adev
;
117 if (acpi_bus_get_device(handle
, &adev
))
122 if (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
, *ts_adev
= NULL
;
142 /* ignore non ACPI devices */
143 handle
= ACPI_HANDLE(dev
);
144 if (!handle
|| acpi_bus_get_device(handle
, &adev
))
147 /* check for LID ACPI switch */
148 if (!strcmp(ACPI_BUTTON_HID_LID
, acpi_device_hid(adev
))) {
149 s3_wmi
.pnp0c0d_adev
= adev
;
153 /* ignore non SPI controllers */
154 if (strncmp(acpi_device_bid(adev
), SPI_CTL_OBJ_NAME
,
155 strlen(SPI_CTL_OBJ_NAME
)))
158 status
= acpi_walk_namespace(ACPI_TYPE_DEVICE
, handle
, 1,
159 s3_wmi_attach_spi_device
, NULL
,
161 if (ACPI_FAILURE(status
))
162 dev_warn(dev
, "failed to enumerate SPI slaves\n");
167 s3_wmi
.touchscreen_adev
= ts_adev
;
172 static int s3_wmi_create_and_register_input(struct platform_device
*pdev
)
174 struct input_dev
*input
;
177 input
= devm_input_allocate_device(&pdev
->dev
);
181 input
->name
= "Lid Switch";
182 input
->phys
= "button/input0";
183 input
->id
.bustype
= BUS_HOST
;
184 input
->id
.product
= 0x0005;
186 input_set_capability(input
, EV_SW
, SW_LID
);
188 error
= input_register_device(input
);
192 s3_wmi
.input
= input
;
196 input_free_device(s3_wmi
.input
);
200 static int __init
s3_wmi_probe(struct platform_device
*pdev
)
204 if (!dmi_check_system(surface3_dmi_table
))
207 memset(&s3_wmi
, 0, sizeof(s3_wmi
));
209 bus_for_each_dev(&platform_bus_type
, NULL
, NULL
,
210 s3_wmi_check_platform_device
);
212 if (!s3_wmi
.touchscreen_adev
)
215 acpi_bus_trim(s3_wmi
.pnp0c0d_adev
);
217 error
= s3_wmi_create_and_register_input(pdev
);
219 goto restore_acpi_lid
;
221 acpi_initialize_hp_context(s3_wmi
.touchscreen_adev
, &s3_wmi
.hp
,
222 s3_wmi_hp_notify
, NULL
);
224 s3_wmi_send_lid_state();
229 acpi_bus_scan(s3_wmi
.pnp0c0d_adev
->handle
);
233 static int s3_wmi_remove(struct platform_device
*device
)
235 /* remove the hotplug context from the acpi device */
236 s3_wmi
.touchscreen_adev
->hp
= NULL
;
238 /* reinstall the actual PNPC0C0D LID default handle */
239 acpi_bus_scan(s3_wmi
.pnp0c0d_adev
->handle
);
243 static int __maybe_unused
s3_wmi_resume(struct device
*dev
)
245 s3_wmi_send_lid_state();
248 static SIMPLE_DEV_PM_OPS(s3_wmi_pm
, NULL
, s3_wmi_resume
);
250 static struct platform_driver s3_wmi_driver
= {
252 .name
= "surface3-wmi",
255 .remove
= s3_wmi_remove
,
258 static int __init
s3_wmi_init(void)
262 s3_wmi_pdev
= platform_device_alloc("surface3-wmi", -1);
266 error
= platform_device_add(s3_wmi_pdev
);
270 error
= platform_driver_probe(&s3_wmi_driver
, s3_wmi_probe
);
274 pr_info("Surface 3 WMI Extras loaded\n");
278 platform_device_del(s3_wmi_pdev
);
280 platform_device_put(s3_wmi_pdev
);
284 static void __exit
s3_wmi_exit(void)
286 platform_device_unregister(s3_wmi_pdev
);
287 platform_driver_unregister(&s3_wmi_driver
);
290 module_init(s3_wmi_init
);
291 module_exit(s3_wmi_exit
);