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 /*
2461cd4822SLejun Zhu  * Definition of buttons on the tablet. The ACPI index of each button
2561cd4822SLejun Zhu  * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
2661cd4822SLejun Zhu  * Platforms"
2761cd4822SLejun Zhu  */
2861cd4822SLejun Zhu #define MAX_NBUTTONS	5
2961cd4822SLejun Zhu 
3061cd4822SLejun Zhu struct soc_button_info {
3161cd4822SLejun Zhu 	const char *name;
3261cd4822SLejun Zhu 	int acpi_index;
3361cd4822SLejun Zhu 	unsigned int event_type;
3461cd4822SLejun Zhu 	unsigned int event_code;
3561cd4822SLejun Zhu 	bool autorepeat;
3661cd4822SLejun Zhu 	bool wakeup;
3761cd4822SLejun Zhu };
3861cd4822SLejun Zhu 
3961cd4822SLejun Zhu /*
4061cd4822SLejun Zhu  * Some of the buttons like volume up/down are auto repeat, while others
4161cd4822SLejun Zhu  * are not. To support both, we register two platform devices, and put
4261cd4822SLejun Zhu  * buttons into them based on whether the key should be auto repeat.
4361cd4822SLejun Zhu  */
4461cd4822SLejun Zhu #define BUTTON_TYPES	2
4561cd4822SLejun Zhu 
4661cd4822SLejun Zhu struct soc_button_data {
4761cd4822SLejun Zhu 	struct platform_device *children[BUTTON_TYPES];
4861cd4822SLejun Zhu };
4961cd4822SLejun Zhu 
5061cd4822SLejun Zhu /*
5161cd4822SLejun Zhu  * Get the Nth GPIO number from the ACPI object.
5261cd4822SLejun Zhu  */
5361cd4822SLejun Zhu static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
5461cd4822SLejun Zhu {
5561cd4822SLejun Zhu 	struct gpio_desc *desc;
5661cd4822SLejun Zhu 	int gpio;
5761cd4822SLejun Zhu 
58a01cd170SHans de Goede 	desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
5961cd4822SLejun Zhu 	if (IS_ERR(desc))
6061cd4822SLejun Zhu 		return PTR_ERR(desc);
6161cd4822SLejun Zhu 
6261cd4822SLejun Zhu 	gpio = desc_to_gpio(desc);
6361cd4822SLejun Zhu 
6461cd4822SLejun Zhu 	gpiod_put(desc);
6561cd4822SLejun Zhu 
6661cd4822SLejun Zhu 	return gpio;
6761cd4822SLejun Zhu }
6861cd4822SLejun Zhu 
6961cd4822SLejun Zhu static struct platform_device *
70042e1c79SJin Yao soc_button_device_create(struct platform_device *pdev,
7161cd4822SLejun Zhu 			 const struct soc_button_info *button_info,
7261cd4822SLejun Zhu 			 bool autorepeat)
7361cd4822SLejun Zhu {
7461cd4822SLejun Zhu 	const struct soc_button_info *info;
7561cd4822SLejun Zhu 	struct platform_device *pd;
7661cd4822SLejun Zhu 	struct gpio_keys_button *gpio_keys;
7761cd4822SLejun Zhu 	struct gpio_keys_platform_data *gpio_keys_pdata;
7861cd4822SLejun Zhu 	int n_buttons = 0;
7961cd4822SLejun Zhu 	int gpio;
8061cd4822SLejun Zhu 	int error;
8161cd4822SLejun Zhu 
8261cd4822SLejun Zhu 	gpio_keys_pdata = devm_kzalloc(&pdev->dev,
8361cd4822SLejun Zhu 				       sizeof(*gpio_keys_pdata) +
8461cd4822SLejun Zhu 					sizeof(*gpio_keys) * MAX_NBUTTONS,
8561cd4822SLejun Zhu 				       GFP_KERNEL);
8691cf07cdSPramod Gurav 	if (!gpio_keys_pdata)
8791cf07cdSPramod Gurav 		return ERR_PTR(-ENOMEM);
8891cf07cdSPramod Gurav 
8961cd4822SLejun Zhu 	gpio_keys = (void *)(gpio_keys_pdata + 1);
9061cd4822SLejun Zhu 
9161cd4822SLejun Zhu 	for (info = button_info; info->name; info++) {
9261cd4822SLejun Zhu 		if (info->autorepeat != autorepeat)
9361cd4822SLejun Zhu 			continue;
9461cd4822SLejun Zhu 
9561cd4822SLejun Zhu 		gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
96be8e7a7eSBenjamin Tissoires 		if (!gpio_is_valid(gpio))
9761cd4822SLejun Zhu 			continue;
9861cd4822SLejun Zhu 
9961cd4822SLejun Zhu 		gpio_keys[n_buttons].type = info->event_type;
10061cd4822SLejun Zhu 		gpio_keys[n_buttons].code = info->event_code;
10161cd4822SLejun Zhu 		gpio_keys[n_buttons].gpio = gpio;
10261cd4822SLejun Zhu 		gpio_keys[n_buttons].active_low = 1;
10361cd4822SLejun Zhu 		gpio_keys[n_buttons].desc = info->name;
10461cd4822SLejun Zhu 		gpio_keys[n_buttons].wakeup = info->wakeup;
1055c4fa2a6SHans de Goede 		/* These devices often use cheap buttons, use 50 ms debounce */
1065c4fa2a6SHans de Goede 		gpio_keys[n_buttons].debounce_interval = 50;
10761cd4822SLejun Zhu 		n_buttons++;
10861cd4822SLejun Zhu 	}
10961cd4822SLejun Zhu 
11061cd4822SLejun Zhu 	if (n_buttons == 0) {
11161cd4822SLejun Zhu 		error = -ENODEV;
11261cd4822SLejun Zhu 		goto err_free_mem;
11361cd4822SLejun Zhu 	}
11461cd4822SLejun Zhu 
11561cd4822SLejun Zhu 	gpio_keys_pdata->buttons = gpio_keys;
11661cd4822SLejun Zhu 	gpio_keys_pdata->nbuttons = n_buttons;
11761cd4822SLejun Zhu 	gpio_keys_pdata->rep = autorepeat;
11861cd4822SLejun Zhu 
11961cd4822SLejun Zhu 	pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO);
12061cd4822SLejun Zhu 	if (!pd) {
12161cd4822SLejun Zhu 		error = -ENOMEM;
12261cd4822SLejun Zhu 		goto err_free_mem;
12361cd4822SLejun Zhu 	}
12461cd4822SLejun Zhu 
12561cd4822SLejun Zhu 	error = platform_device_add_data(pd, gpio_keys_pdata,
12661cd4822SLejun Zhu 					 sizeof(*gpio_keys_pdata));
12761cd4822SLejun Zhu 	if (error)
12861cd4822SLejun Zhu 		goto err_free_pdev;
12961cd4822SLejun Zhu 
13061cd4822SLejun Zhu 	error = platform_device_add(pd);
13161cd4822SLejun Zhu 	if (error)
13261cd4822SLejun Zhu 		goto err_free_pdev;
13361cd4822SLejun Zhu 
13461cd4822SLejun Zhu 	return pd;
13561cd4822SLejun Zhu 
13661cd4822SLejun Zhu err_free_pdev:
13761cd4822SLejun Zhu 	platform_device_put(pd);
13861cd4822SLejun Zhu err_free_mem:
13961cd4822SLejun Zhu 	devm_kfree(&pdev->dev, gpio_keys_pdata);
14061cd4822SLejun Zhu 	return ERR_PTR(error);
14161cd4822SLejun Zhu }
14261cd4822SLejun Zhu 
143042e1c79SJin Yao static int soc_button_remove(struct platform_device *pdev)
14461cd4822SLejun Zhu {
145042e1c79SJin Yao 	struct soc_button_data *priv = platform_get_drvdata(pdev);
146042e1c79SJin Yao 
14761cd4822SLejun Zhu 	int i;
14861cd4822SLejun Zhu 
14961cd4822SLejun Zhu 	for (i = 0; i < BUTTON_TYPES; i++)
15061cd4822SLejun Zhu 		if (priv->children[i])
15161cd4822SLejun Zhu 			platform_device_unregister(priv->children[i]);
152042e1c79SJin Yao 
153042e1c79SJin Yao 	return 0;
15461cd4822SLejun Zhu }
15561cd4822SLejun Zhu 
156042e1c79SJin Yao static int soc_button_probe(struct platform_device *pdev)
15761cd4822SLejun Zhu {
158042e1c79SJin Yao 	struct device *dev = &pdev->dev;
159042e1c79SJin Yao 	const struct acpi_device_id *id;
160042e1c79SJin Yao 	struct soc_button_info *button_info;
16161cd4822SLejun Zhu 	struct soc_button_data *priv;
16261cd4822SLejun Zhu 	struct platform_device *pd;
16361cd4822SLejun Zhu 	int i;
16461cd4822SLejun Zhu 	int error;
16561cd4822SLejun Zhu 
166042e1c79SJin Yao 	id = acpi_match_device(dev->driver->acpi_match_table, dev);
167042e1c79SJin Yao 	if (!id)
168042e1c79SJin Yao 		return -ENODEV;
169042e1c79SJin Yao 
170042e1c79SJin Yao 	button_info = (struct soc_button_info *)id->driver_data;
171042e1c79SJin Yao 
172a01cd170SHans de Goede 	if (gpiod_count(dev, NULL) <= 0) {
173aa45590aSGuenter Roeck 		dev_dbg(dev, "no GPIO attached, ignoring...\n");
1743d5a9437SBenjamin Tissoires 		return -ENODEV;
1753d5a9437SBenjamin Tissoires 	}
1763d5a9437SBenjamin Tissoires 
177aa45590aSGuenter Roeck 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
17861cd4822SLejun Zhu 	if (!priv)
17961cd4822SLejun Zhu 		return -ENOMEM;
18061cd4822SLejun Zhu 
181042e1c79SJin Yao 	platform_set_drvdata(pdev, priv);
18261cd4822SLejun Zhu 
18361cd4822SLejun Zhu 	for (i = 0; i < BUTTON_TYPES; i++) {
18461cd4822SLejun Zhu 		pd = soc_button_device_create(pdev, button_info, i == 0);
18561cd4822SLejun Zhu 		if (IS_ERR(pd)) {
18661cd4822SLejun Zhu 			error = PTR_ERR(pd);
18761cd4822SLejun Zhu 			if (error != -ENODEV) {
18861cd4822SLejun Zhu 				soc_button_remove(pdev);
18961cd4822SLejun Zhu 				return error;
19061cd4822SLejun Zhu 			}
1917740fc52SLejun Zhu 			continue;
19261cd4822SLejun Zhu 		}
19361cd4822SLejun Zhu 
19461cd4822SLejun Zhu 		priv->children[i] = pd;
19561cd4822SLejun Zhu 	}
19661cd4822SLejun Zhu 
19761cd4822SLejun Zhu 	if (!priv->children[0] && !priv->children[1])
19861cd4822SLejun Zhu 		return -ENODEV;
19961cd4822SLejun Zhu 
20061cd4822SLejun Zhu 	return 0;
20161cd4822SLejun Zhu }
20261cd4822SLejun Zhu 
20361cd4822SLejun Zhu static struct soc_button_info soc_button_PNP0C40[] = {
20461cd4822SLejun Zhu 	{ "power", 0, EV_KEY, KEY_POWER, false, true },
205791738beSBastien Nocera 	{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
20661cd4822SLejun Zhu 	{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
20761cd4822SLejun Zhu 	{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
20861cd4822SLejun Zhu 	{ "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
20961cd4822SLejun Zhu 	{ }
21061cd4822SLejun Zhu };
21161cd4822SLejun Zhu 
212042e1c79SJin Yao static const struct acpi_device_id soc_button_acpi_match[] = {
213042e1c79SJin Yao 	{ "PNP0C40", (unsigned long)soc_button_PNP0C40 },
214042e1c79SJin Yao 	{ }
21561cd4822SLejun Zhu };
21661cd4822SLejun Zhu 
217042e1c79SJin Yao MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match);
218042e1c79SJin Yao 
219042e1c79SJin Yao static struct platform_driver soc_button_driver = {
220042e1c79SJin Yao 	.probe          = soc_button_probe,
22161cd4822SLejun Zhu 	.remove		= soc_button_remove,
222042e1c79SJin Yao 	.driver		= {
223042e1c79SJin Yao 		.name = KBUILD_MODNAME,
224042e1c79SJin Yao 		.acpi_match_table = ACPI_PTR(soc_button_acpi_match),
225042e1c79SJin Yao 	},
22661cd4822SLejun Zhu };
227042e1c79SJin Yao module_platform_driver(soc_button_driver);
22861cd4822SLejun Zhu 
22961cd4822SLejun Zhu MODULE_LICENSE("GPL");
230