161cd4822SLejun Zhu /*
261cd4822SLejun Zhu  * Supports for the button array on SoC tablets originally running
361cd4822SLejun Zhu  * Windows 8.
461cd4822SLejun Zhu  *
561cd4822SLejun Zhu  * (C) Copyright 2014 Intel Corporation
661cd4822SLejun Zhu  *
761cd4822SLejun Zhu  * This program is free software; you can redistribute it and/or
861cd4822SLejun Zhu  * modify it under the terms of the GNU General Public License
961cd4822SLejun Zhu  * as published by the Free Software Foundation; version 2
1061cd4822SLejun Zhu  * of the License.
1161cd4822SLejun Zhu  */
1261cd4822SLejun Zhu 
1361cd4822SLejun Zhu #include <linux/module.h>
1461cd4822SLejun Zhu #include <linux/input.h>
1561cd4822SLejun Zhu #include <linux/init.h>
1661cd4822SLejun Zhu #include <linux/kernel.h>
1761cd4822SLejun Zhu #include <linux/acpi.h>
1861cd4822SLejun Zhu #include <linux/gpio/consumer.h>
1961cd4822SLejun Zhu #include <linux/gpio_keys.h>
20be8e7a7eSBenjamin Tissoires #include <linux/gpio.h>
2161cd4822SLejun Zhu #include <linux/platform_device.h>
2261cd4822SLejun Zhu 
2361cd4822SLejun Zhu struct soc_button_info {
2461cd4822SLejun Zhu 	const char *name;
2561cd4822SLejun Zhu 	int acpi_index;
2661cd4822SLejun Zhu 	unsigned int event_type;
2761cd4822SLejun Zhu 	unsigned int event_code;
2861cd4822SLejun Zhu 	bool autorepeat;
2961cd4822SLejun Zhu 	bool wakeup;
3061cd4822SLejun Zhu };
3161cd4822SLejun Zhu 
3261cd4822SLejun Zhu /*
3361cd4822SLejun Zhu  * Some of the buttons like volume up/down are auto repeat, while others
3461cd4822SLejun Zhu  * are not. To support both, we register two platform devices, and put
3561cd4822SLejun Zhu  * buttons into them based on whether the key should be auto repeat.
3661cd4822SLejun Zhu  */
3761cd4822SLejun Zhu #define BUTTON_TYPES	2
3861cd4822SLejun Zhu 
3961cd4822SLejun Zhu struct soc_button_data {
4061cd4822SLejun Zhu 	struct platform_device *children[BUTTON_TYPES];
4161cd4822SLejun Zhu };
4261cd4822SLejun Zhu 
4361cd4822SLejun Zhu /*
4461cd4822SLejun Zhu  * Get the Nth GPIO number from the ACPI object.
4561cd4822SLejun Zhu  */
4661cd4822SLejun Zhu static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
4761cd4822SLejun Zhu {
4861cd4822SLejun Zhu 	struct gpio_desc *desc;
4961cd4822SLejun Zhu 	int gpio;
5061cd4822SLejun Zhu 
51a01cd170SHans de Goede 	desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
5261cd4822SLejun Zhu 	if (IS_ERR(desc))
5361cd4822SLejun Zhu 		return PTR_ERR(desc);
5461cd4822SLejun Zhu 
5561cd4822SLejun Zhu 	gpio = desc_to_gpio(desc);
5661cd4822SLejun Zhu 
5761cd4822SLejun Zhu 	gpiod_put(desc);
5861cd4822SLejun Zhu 
5961cd4822SLejun Zhu 	return gpio;
6061cd4822SLejun Zhu }
6161cd4822SLejun Zhu 
6261cd4822SLejun Zhu static struct platform_device *
63042e1c79SJin Yao soc_button_device_create(struct platform_device *pdev,
6461cd4822SLejun Zhu 			 const struct soc_button_info *button_info,
6561cd4822SLejun Zhu 			 bool autorepeat)
6661cd4822SLejun Zhu {
6761cd4822SLejun Zhu 	const struct soc_button_info *info;
6861cd4822SLejun Zhu 	struct platform_device *pd;
6961cd4822SLejun Zhu 	struct gpio_keys_button *gpio_keys;
7061cd4822SLejun Zhu 	struct gpio_keys_platform_data *gpio_keys_pdata;
7161cd4822SLejun Zhu 	int n_buttons = 0;
7261cd4822SLejun Zhu 	int gpio;
7361cd4822SLejun Zhu 	int error;
7461cd4822SLejun Zhu 
757283b47dSHans de Goede 	for (info = button_info; info->name; info++)
767283b47dSHans de Goede 		if (info->autorepeat == autorepeat)
777283b47dSHans de Goede 			n_buttons++;
787283b47dSHans de Goede 
7961cd4822SLejun Zhu 	gpio_keys_pdata = devm_kzalloc(&pdev->dev,
8061cd4822SLejun Zhu 				       sizeof(*gpio_keys_pdata) +
817283b47dSHans de Goede 					sizeof(*gpio_keys) * n_buttons,
8261cd4822SLejun Zhu 				       GFP_KERNEL);
8391cf07cdSPramod Gurav 	if (!gpio_keys_pdata)
8491cf07cdSPramod Gurav 		return ERR_PTR(-ENOMEM);
8591cf07cdSPramod Gurav 
8661cd4822SLejun Zhu 	gpio_keys = (void *)(gpio_keys_pdata + 1);
877283b47dSHans de Goede 	n_buttons = 0;
8861cd4822SLejun Zhu 
8961cd4822SLejun Zhu 	for (info = button_info; info->name; info++) {
9061cd4822SLejun Zhu 		if (info->autorepeat != autorepeat)
9161cd4822SLejun Zhu 			continue;
9261cd4822SLejun Zhu 
9361cd4822SLejun Zhu 		gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
94be8e7a7eSBenjamin Tissoires 		if (!gpio_is_valid(gpio))
9561cd4822SLejun Zhu 			continue;
9661cd4822SLejun Zhu 
9761cd4822SLejun Zhu 		gpio_keys[n_buttons].type = info->event_type;
9861cd4822SLejun Zhu 		gpio_keys[n_buttons].code = info->event_code;
9961cd4822SLejun Zhu 		gpio_keys[n_buttons].gpio = gpio;
10061cd4822SLejun Zhu 		gpio_keys[n_buttons].active_low = 1;
10161cd4822SLejun Zhu 		gpio_keys[n_buttons].desc = info->name;
10261cd4822SLejun Zhu 		gpio_keys[n_buttons].wakeup = info->wakeup;
1035c4fa2a6SHans de Goede 		/* These devices often use cheap buttons, use 50 ms debounce */
1045c4fa2a6SHans de Goede 		gpio_keys[n_buttons].debounce_interval = 50;
10561cd4822SLejun Zhu 		n_buttons++;
10661cd4822SLejun Zhu 	}
10761cd4822SLejun Zhu 
10861cd4822SLejun Zhu 	if (n_buttons == 0) {
10961cd4822SLejun Zhu 		error = -ENODEV;
11061cd4822SLejun Zhu 		goto err_free_mem;
11161cd4822SLejun Zhu 	}
11261cd4822SLejun Zhu 
11361cd4822SLejun Zhu 	gpio_keys_pdata->buttons = gpio_keys;
11461cd4822SLejun Zhu 	gpio_keys_pdata->nbuttons = n_buttons;
11561cd4822SLejun Zhu 	gpio_keys_pdata->rep = autorepeat;
11661cd4822SLejun Zhu 
11761cd4822SLejun Zhu 	pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO);
11861cd4822SLejun Zhu 	if (!pd) {
11961cd4822SLejun Zhu 		error = -ENOMEM;
12061cd4822SLejun Zhu 		goto err_free_mem;
12161cd4822SLejun Zhu 	}
12261cd4822SLejun Zhu 
12361cd4822SLejun Zhu 	error = platform_device_add_data(pd, gpio_keys_pdata,
12461cd4822SLejun Zhu 					 sizeof(*gpio_keys_pdata));
12561cd4822SLejun Zhu 	if (error)
12661cd4822SLejun Zhu 		goto err_free_pdev;
12761cd4822SLejun Zhu 
12861cd4822SLejun Zhu 	error = platform_device_add(pd);
12961cd4822SLejun Zhu 	if (error)
13061cd4822SLejun Zhu 		goto err_free_pdev;
13161cd4822SLejun Zhu 
13261cd4822SLejun Zhu 	return pd;
13361cd4822SLejun Zhu 
13461cd4822SLejun Zhu err_free_pdev:
13561cd4822SLejun Zhu 	platform_device_put(pd);
13661cd4822SLejun Zhu err_free_mem:
13761cd4822SLejun Zhu 	devm_kfree(&pdev->dev, gpio_keys_pdata);
13861cd4822SLejun Zhu 	return ERR_PTR(error);
13961cd4822SLejun Zhu }
14061cd4822SLejun Zhu 
141042e1c79SJin Yao static int soc_button_remove(struct platform_device *pdev)
14261cd4822SLejun Zhu {
143042e1c79SJin Yao 	struct soc_button_data *priv = platform_get_drvdata(pdev);
144042e1c79SJin Yao 
14561cd4822SLejun Zhu 	int i;
14661cd4822SLejun Zhu 
14761cd4822SLejun Zhu 	for (i = 0; i < BUTTON_TYPES; i++)
14861cd4822SLejun Zhu 		if (priv->children[i])
14961cd4822SLejun Zhu 			platform_device_unregister(priv->children[i]);
150042e1c79SJin Yao 
151042e1c79SJin Yao 	return 0;
15261cd4822SLejun Zhu }
15361cd4822SLejun Zhu 
154042e1c79SJin Yao static int soc_button_probe(struct platform_device *pdev)
15561cd4822SLejun Zhu {
156042e1c79SJin Yao 	struct device *dev = &pdev->dev;
157042e1c79SJin Yao 	const struct acpi_device_id *id;
158042e1c79SJin Yao 	struct soc_button_info *button_info;
15961cd4822SLejun Zhu 	struct soc_button_data *priv;
16061cd4822SLejun Zhu 	struct platform_device *pd;
16161cd4822SLejun Zhu 	int i;
16261cd4822SLejun Zhu 	int error;
16361cd4822SLejun Zhu 
164042e1c79SJin Yao 	id = acpi_match_device(dev->driver->acpi_match_table, dev);
165042e1c79SJin Yao 	if (!id)
166042e1c79SJin Yao 		return -ENODEV;
167042e1c79SJin Yao 
168042e1c79SJin Yao 	button_info = (struct soc_button_info *)id->driver_data;
169042e1c79SJin Yao 
170a01cd170SHans de Goede 	if (gpiod_count(dev, NULL) <= 0) {
171aa45590aSGuenter Roeck 		dev_dbg(dev, "no GPIO attached, ignoring...\n");
1723d5a9437SBenjamin Tissoires 		return -ENODEV;
1733d5a9437SBenjamin Tissoires 	}
1743d5a9437SBenjamin Tissoires 
175aa45590aSGuenter Roeck 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
17661cd4822SLejun Zhu 	if (!priv)
17761cd4822SLejun Zhu 		return -ENOMEM;
17861cd4822SLejun Zhu 
179042e1c79SJin Yao 	platform_set_drvdata(pdev, priv);
18061cd4822SLejun Zhu 
18161cd4822SLejun Zhu 	for (i = 0; i < BUTTON_TYPES; i++) {
18261cd4822SLejun Zhu 		pd = soc_button_device_create(pdev, button_info, i == 0);
18361cd4822SLejun Zhu 		if (IS_ERR(pd)) {
18461cd4822SLejun Zhu 			error = PTR_ERR(pd);
18561cd4822SLejun Zhu 			if (error != -ENODEV) {
18661cd4822SLejun Zhu 				soc_button_remove(pdev);
18761cd4822SLejun Zhu 				return error;
18861cd4822SLejun Zhu 			}
1897740fc52SLejun Zhu 			continue;
19061cd4822SLejun Zhu 		}
19161cd4822SLejun Zhu 
19261cd4822SLejun Zhu 		priv->children[i] = pd;
19361cd4822SLejun Zhu 	}
19461cd4822SLejun Zhu 
19561cd4822SLejun Zhu 	if (!priv->children[0] && !priv->children[1])
19661cd4822SLejun Zhu 		return -ENODEV;
19761cd4822SLejun Zhu 
19861cd4822SLejun Zhu 	return 0;
19961cd4822SLejun Zhu }
20061cd4822SLejun Zhu 
2017283b47dSHans de Goede /*
2027283b47dSHans de Goede  * Definition of buttons on the tablet. The ACPI index of each button
2037283b47dSHans de Goede  * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
2047283b47dSHans de Goede  * Platforms"
2057283b47dSHans de Goede  */
20661cd4822SLejun Zhu static struct soc_button_info soc_button_PNP0C40[] = {
20761cd4822SLejun Zhu 	{ "power", 0, EV_KEY, KEY_POWER, false, true },
208791738beSBastien Nocera 	{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
20961cd4822SLejun Zhu 	{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
21061cd4822SLejun Zhu 	{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
21161cd4822SLejun Zhu 	{ "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
21261cd4822SLejun Zhu 	{ }
21361cd4822SLejun Zhu };
21461cd4822SLejun Zhu 
215042e1c79SJin Yao static const struct acpi_device_id soc_button_acpi_match[] = {
216042e1c79SJin Yao 	{ "PNP0C40", (unsigned long)soc_button_PNP0C40 },
217042e1c79SJin Yao 	{ }
21861cd4822SLejun Zhu };
21961cd4822SLejun Zhu 
220042e1c79SJin Yao MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match);
221042e1c79SJin Yao 
222042e1c79SJin Yao static struct platform_driver soc_button_driver = {
223042e1c79SJin Yao 	.probe          = soc_button_probe,
22461cd4822SLejun Zhu 	.remove		= soc_button_remove,
225042e1c79SJin Yao 	.driver		= {
226042e1c79SJin Yao 		.name = KBUILD_MODNAME,
227042e1c79SJin Yao 		.acpi_match_table = ACPI_PTR(soc_button_acpi_match),
228042e1c79SJin Yao 	},
22961cd4822SLejun Zhu };
230042e1c79SJin Yao module_platform_driver(soc_button_driver);
23161cd4822SLejun Zhu 
23261cd4822SLejun Zhu MODULE_LICENSE("GPL");
233