@@ -25,6 +25,11 @@ struct soc_button_info {
25
25
bool wakeup ;
26
26
};
27
27
28
+ struct soc_device_data {
29
+ const struct soc_button_info * button_info ;
30
+ int (* check )(struct device * dev );
31
+ };
32
+
28
33
/*
29
34
* Some of the buttons like volume up/down are auto repeat, while others
30
35
* are not. To support both, we register two platform devices, and put
@@ -87,8 +92,13 @@ soc_button_device_create(struct platform_device *pdev,
87
92
continue ;
88
93
89
94
gpio = soc_button_lookup_gpio (& pdev -> dev , info -> acpi_index );
90
- if (!gpio_is_valid (gpio ))
95
+ if (gpio < 0 && gpio != - ENOENT ) {
96
+ error = gpio ;
97
+ goto err_free_mem ;
98
+ } else if (!gpio_is_valid (gpio )) {
99
+ /* Skip GPIO if not present */
91
100
continue ;
101
+ }
92
102
93
103
gpio_keys [n_buttons ].type = info -> event_type ;
94
104
gpio_keys [n_buttons ].code = info -> event_code ;
@@ -309,23 +319,26 @@ static int soc_button_remove(struct platform_device *pdev)
309
319
static int soc_button_probe (struct platform_device * pdev )
310
320
{
311
321
struct device * dev = & pdev -> dev ;
312
- const struct acpi_device_id * id ;
313
- struct soc_button_info * button_info ;
322
+ const struct soc_device_data * device_data ;
323
+ const struct soc_button_info * button_info ;
314
324
struct soc_button_data * priv ;
315
325
struct platform_device * pd ;
316
326
int i ;
317
327
int error ;
318
328
319
- id = acpi_match_device (dev -> driver -> acpi_match_table , dev );
320
- if (!id )
321
- return - ENODEV ;
329
+ device_data = acpi_device_get_match_data (dev );
330
+ if (device_data && device_data -> check ) {
331
+ error = device_data -> check (dev );
332
+ if (error )
333
+ return error ;
334
+ }
322
335
323
- if (!id -> driver_data ) {
336
+ if (device_data && device_data -> button_info ) {
337
+ button_info = device_data -> button_info ;
338
+ } else {
324
339
button_info = soc_button_get_button_info (dev );
325
340
if (IS_ERR (button_info ))
326
341
return PTR_ERR (button_info );
327
- } else {
328
- button_info = (struct soc_button_info * )id -> driver_data ;
329
342
}
330
343
331
344
error = gpiod_count (dev , NULL );
@@ -357,7 +370,7 @@ static int soc_button_probe(struct platform_device *pdev)
357
370
if (!priv -> children [0 ] && !priv -> children [1 ])
358
371
return - ENODEV ;
359
372
360
- if (!id -> driver_data )
373
+ if (!device_data || ! device_data -> button_info )
361
374
devm_kfree (dev , button_info );
362
375
363
376
return 0 ;
@@ -368,7 +381,7 @@ static int soc_button_probe(struct platform_device *pdev)
368
381
* is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
369
382
* Platforms"
370
383
*/
371
- static struct soc_button_info soc_button_PNP0C40 [] = {
384
+ static const struct soc_button_info soc_button_PNP0C40 [] = {
372
385
{ "power" , 0 , EV_KEY , KEY_POWER , false, true },
373
386
{ "home" , 1 , EV_KEY , KEY_LEFTMETA , false, true },
374
387
{ "volume_up" , 2 , EV_KEY , KEY_VOLUMEUP , true, false },
@@ -377,9 +390,77 @@ static struct soc_button_info soc_button_PNP0C40[] = {
377
390
{ }
378
391
};
379
392
393
+ static const struct soc_device_data soc_device_PNP0C40 = {
394
+ .button_info = soc_button_PNP0C40 ,
395
+ };
396
+
397
+ /*
398
+ * Special device check for Surface Book 2 and Surface Pro (2017).
399
+ * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
400
+ * devices use MSHW0040 for power and volume buttons, however the way they
401
+ * have to be addressed differs. Make sure that we only load this drivers
402
+ * for the correct devices by checking the OEM Platform Revision provided by
403
+ * the _DSM method.
404
+ */
405
+ #define MSHW0040_DSM_REVISION 0x01
406
+ #define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision
407
+ static const guid_t MSHW0040_DSM_UUID =
408
+ GUID_INIT (0x6fd05c69 , 0xcde3 , 0x49f4 , 0x95 , 0xed , 0xab , 0x16 , 0x65 ,
409
+ 0x49 , 0x80 , 0x35 );
410
+
411
+ static int soc_device_check_MSHW0040 (struct device * dev )
412
+ {
413
+ acpi_handle handle = ACPI_HANDLE (dev );
414
+ union acpi_object * result ;
415
+ u64 oem_platform_rev = 0 ; // valid revisions are nonzero
416
+
417
+ // get OEM platform revision
418
+ result = acpi_evaluate_dsm_typed (handle , & MSHW0040_DSM_UUID ,
419
+ MSHW0040_DSM_REVISION ,
420
+ MSHW0040_DSM_GET_OMPR , NULL ,
421
+ ACPI_TYPE_INTEGER );
422
+
423
+ if (result ) {
424
+ oem_platform_rev = result -> integer .value ;
425
+ ACPI_FREE (result );
426
+ }
427
+
428
+ /*
429
+ * If the revision is zero here, the _DSM evaluation has failed. This
430
+ * indicates that we have a Pro 4 or Book 1 and this driver should not
431
+ * be used.
432
+ */
433
+ if (oem_platform_rev == 0 )
434
+ return - ENODEV ;
435
+
436
+ dev_dbg (dev , "OEM Platform Revision %llu\n" , oem_platform_rev );
437
+
438
+ return 0 ;
439
+ }
440
+
441
+ /*
442
+ * Button infos for Microsoft Surface Book 2 and Surface Pro (2017).
443
+ * Obtained from DSDT/testing.
444
+ */
445
+ static const struct soc_button_info soc_button_MSHW0040 [] = {
446
+ { "power" , 0 , EV_KEY , KEY_POWER , false, true },
447
+ { "volume_up" , 2 , EV_KEY , KEY_VOLUMEUP , true, false },
448
+ { "volume_down" , 4 , EV_KEY , KEY_VOLUMEDOWN , true, false },
449
+ { }
450
+ };
451
+
452
+ static const struct soc_device_data soc_device_MSHW0040 = {
453
+ .button_info = soc_button_MSHW0040 ,
454
+ .check = soc_device_check_MSHW0040 ,
455
+ };
456
+
380
457
static const struct acpi_device_id soc_button_acpi_match [] = {
381
- { "PNP0C40" , (unsigned long )soc_button_PNP0C40 },
458
+ { "PNP0C40" , (unsigned long )& soc_device_PNP0C40 },
382
459
{ "ACPI0011" , 0 },
460
+
461
+ /* Microsoft Surface Devices (5th and 6th generation) */
462
+ { "MSHW0040" , (unsigned long )& soc_device_MSHW0040 },
463
+
383
464
{ }
384
465
};
385
466
0 commit comments