diff options
Diffstat (limited to 'drivers/input/misc/soc_button_array.c')
| -rw-r--r-- | drivers/input/misc/soc_button_array.c | 127 | 
1 files changed, 101 insertions, 26 deletions
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 5e59f8e57f8e..97e3639e99d0 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -25,6 +25,11 @@ struct soc_button_info {  	bool wakeup;  }; +struct soc_device_data { +	const struct soc_button_info *button_info; +	int (*check)(struct device *dev); +}; +  /*   * Some of the buttons like volume up/down are auto repeat, while others   * are not. To support both, we register two platform devices, and put @@ -87,8 +92,13 @@ soc_button_device_create(struct platform_device *pdev,  			continue;  		gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); -		if (!gpio_is_valid(gpio)) +		if (gpio < 0 && gpio != -ENOENT) { +			error = gpio; +			goto err_free_mem; +		} else if (!gpio_is_valid(gpio)) { +			/* Skip GPIO if not present */  			continue; +		}  		gpio_keys[n_buttons].type = info->event_type;  		gpio_keys[n_buttons].code = info->event_code; @@ -110,25 +120,19 @@ soc_button_device_create(struct platform_device *pdev,  	gpio_keys_pdata->nbuttons = n_buttons;  	gpio_keys_pdata->rep = autorepeat; -	pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); -	if (!pd) { -		error = -ENOMEM; +	pd = platform_device_register_resndata(&pdev->dev, "gpio-keys", +					       PLATFORM_DEVID_AUTO, NULL, 0, +					       gpio_keys_pdata, +					       sizeof(*gpio_keys_pdata)); +	error = PTR_ERR_OR_ZERO(pd); +	if (error) { +		dev_err(&pdev->dev, +			"failed registering gpio-keys: %d\n", error);  		goto err_free_mem;  	} -	error = platform_device_add_data(pd, gpio_keys_pdata, -					 sizeof(*gpio_keys_pdata)); -	if (error) -		goto err_free_pdev; - -	error = platform_device_add(pd); -	if (error) -		goto err_free_pdev; -  	return pd; -err_free_pdev: -	platform_device_put(pd);  err_free_mem:  	devm_kfree(&pdev->dev, gpio_keys_pdata);  	return ERR_PTR(error); @@ -309,23 +313,26 @@ static int soc_button_remove(struct platform_device *pdev)  static int soc_button_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; -	const struct acpi_device_id *id; -	struct soc_button_info *button_info; +	const struct soc_device_data *device_data; +	const struct soc_button_info *button_info;  	struct soc_button_data *priv;  	struct platform_device *pd;  	int i;  	int error; -	id = acpi_match_device(dev->driver->acpi_match_table, dev); -	if (!id) -		return -ENODEV; +	device_data = acpi_device_get_match_data(dev); +	if (device_data && device_data->check) { +		error = device_data->check(dev); +		if (error) +			return error; +	} -	if (!id->driver_data) { +	if (device_data && device_data->button_info) { +		button_info = device_data->button_info; +	} else {  		button_info = soc_button_get_button_info(dev);  		if (IS_ERR(button_info))  			return PTR_ERR(button_info); -	} else { -		button_info = (struct soc_button_info *)id->driver_data;  	}  	error = gpiod_count(dev, NULL); @@ -357,7 +364,7 @@ static int soc_button_probe(struct platform_device *pdev)  	if (!priv->children[0] && !priv->children[1])  		return -ENODEV; -	if (!id->driver_data) +	if (!device_data || !device_data->button_info)  		devm_kfree(dev, button_info);  	return 0; @@ -368,7 +375,7 @@ static int soc_button_probe(struct platform_device *pdev)   * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC   * Platforms"   */ -static struct soc_button_info soc_button_PNP0C40[] = { +static const struct soc_button_info soc_button_PNP0C40[] = {  	{ "power", 0, EV_KEY, KEY_POWER, false, true },  	{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },  	{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, @@ -377,9 +384,77 @@ static struct soc_button_info soc_button_PNP0C40[] = {  	{ }  }; +static const struct soc_device_data soc_device_PNP0C40 = { +	.button_info = soc_button_PNP0C40, +}; + +/* + * Special device check for Surface Book 2 and Surface Pro (2017). + * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned + * devices use MSHW0040 for power and volume buttons, however the way they + * have to be addressed differs. Make sure that we only load this drivers + * for the correct devices by checking the OEM Platform Revision provided by + * the _DSM method. + */ +#define MSHW0040_DSM_REVISION		0x01 +#define MSHW0040_DSM_GET_OMPR		0x02	// get OEM Platform Revision +static const guid_t MSHW0040_DSM_UUID = +	GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, +		  0x49, 0x80, 0x35); + +static int soc_device_check_MSHW0040(struct device *dev) +{ +	acpi_handle handle = ACPI_HANDLE(dev); +	union acpi_object *result; +	u64 oem_platform_rev = 0;	// valid revisions are nonzero + +	// get OEM platform revision +	result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, +					 MSHW0040_DSM_REVISION, +					 MSHW0040_DSM_GET_OMPR, NULL, +					 ACPI_TYPE_INTEGER); + +	if (result) { +		oem_platform_rev = result->integer.value; +		ACPI_FREE(result); +	} + +	/* +	 * If the revision is zero here, the _DSM evaluation has failed. This +	 * indicates that we have a Pro 4 or Book 1 and this driver should not +	 * be used. +	 */ +	if (oem_platform_rev == 0) +		return -ENODEV; + +	dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); + +	return 0; +} + +/* + * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). + * Obtained from DSDT/testing. + */ +static const struct soc_button_info soc_button_MSHW0040[] = { +	{ "power", 0, EV_KEY, KEY_POWER, false, true }, +	{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, +	{ "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false }, +	{ } +}; + +static const struct soc_device_data soc_device_MSHW0040 = { +	.button_info = soc_button_MSHW0040, +	.check = soc_device_check_MSHW0040, +}; +  static const struct acpi_device_id soc_button_acpi_match[] = { -	{ "PNP0C40", (unsigned long)soc_button_PNP0C40 }, +	{ "PNP0C40", (unsigned long)&soc_device_PNP0C40 },  	{ "ACPI0011", 0 }, + +	/* Microsoft Surface Devices (5th and 6th generation) */ +	{ "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, +  	{ }  };  |