xref: /linux/drivers/input/mouse/gpio_mouse.c (revision 981facf94a18f1b8d4989087ba6d7cedae1929ec)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25f565502SHans-Christian Egtvedt /*
35f565502SHans-Christian Egtvedt  * Driver for simulating a mouse on GPIO lines.
45f565502SHans-Christian Egtvedt  *
55f565502SHans-Christian Egtvedt  * Copyright (C) 2007 Atmel Corporation
6836bd419SLinus Walleij  * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
75f565502SHans-Christian Egtvedt  */
85f565502SHans-Christian Egtvedt 
95f565502SHans-Christian Egtvedt #include <linux/module.h>
105f565502SHans-Christian Egtvedt #include <linux/platform_device.h>
113df955f8SDmitry Torokhov #include <linux/input.h>
12836bd419SLinus Walleij #include <linux/gpio/consumer.h>
13836bd419SLinus Walleij #include <linux/property.h>
14adb77b3eSLinus Walleij #include <linux/of.h>
15c5053e69SLinus Walleij 
16c5053e69SLinus Walleij /**
1734cf5a1cSLinus Walleij  * struct gpio_mouse
18836bd419SLinus Walleij  * @scan_ms: the scan interval in milliseconds.
19c5053e69SLinus Walleij  * @up: GPIO line for up value.
20c5053e69SLinus Walleij  * @down: GPIO line for down value.
21c5053e69SLinus Walleij  * @left: GPIO line for left value.
22c5053e69SLinus Walleij  * @right: GPIO line for right value.
23c5053e69SLinus Walleij  * @bleft: GPIO line for left button.
24c5053e69SLinus Walleij  * @bmiddle: GPIO line for middle button.
25c5053e69SLinus Walleij  * @bright: GPIO line for right button.
26c5053e69SLinus Walleij  *
27c5053e69SLinus Walleij  * This struct must be added to the platform_device in the board code.
28c5053e69SLinus Walleij  * It is used by the gpio_mouse driver to setup GPIO lines and to
29c5053e69SLinus Walleij  * calculate mouse movement.
30c5053e69SLinus Walleij  */
3134cf5a1cSLinus Walleij struct gpio_mouse {
32836bd419SLinus Walleij 	u32 scan_ms;
33836bd419SLinus Walleij 	struct gpio_desc *up;
34836bd419SLinus Walleij 	struct gpio_desc *down;
35836bd419SLinus Walleij 	struct gpio_desc *left;
36836bd419SLinus Walleij 	struct gpio_desc *right;
37836bd419SLinus Walleij 	struct gpio_desc *bleft;
38836bd419SLinus Walleij 	struct gpio_desc *bmiddle;
39836bd419SLinus Walleij 	struct gpio_desc *bright;
40c5053e69SLinus Walleij };
415f565502SHans-Christian Egtvedt 
425f565502SHans-Christian Egtvedt /*
435f565502SHans-Christian Egtvedt  * Timer function which is run every scan_ms ms when the device is opened.
44*981facf9SJiang Jian  * The dev input variable is set to the input_dev pointer.
455f565502SHans-Christian Egtvedt  */
463df955f8SDmitry Torokhov static void gpio_mouse_scan(struct input_dev *input)
475f565502SHans-Christian Egtvedt {
483df955f8SDmitry Torokhov 	struct gpio_mouse *gpio = input_get_drvdata(input);
495f565502SHans-Christian Egtvedt 	int x, y;
505f565502SHans-Christian Egtvedt 
51836bd419SLinus Walleij 	if (gpio->bleft)
525f565502SHans-Christian Egtvedt 		input_report_key(input, BTN_LEFT,
53836bd419SLinus Walleij 				 gpiod_get_value(gpio->bleft));
54836bd419SLinus Walleij 	if (gpio->bmiddle)
555f565502SHans-Christian Egtvedt 		input_report_key(input, BTN_MIDDLE,
56836bd419SLinus Walleij 				 gpiod_get_value(gpio->bmiddle));
57836bd419SLinus Walleij 	if (gpio->bright)
585f565502SHans-Christian Egtvedt 		input_report_key(input, BTN_RIGHT,
59836bd419SLinus Walleij 				 gpiod_get_value(gpio->bright));
605f565502SHans-Christian Egtvedt 
61836bd419SLinus Walleij 	x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left);
62836bd419SLinus Walleij 	y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up);
635f565502SHans-Christian Egtvedt 
645f565502SHans-Christian Egtvedt 	input_report_rel(input, REL_X, x);
655f565502SHans-Christian Egtvedt 	input_report_rel(input, REL_Y, y);
665f565502SHans-Christian Egtvedt 	input_sync(input);
675f565502SHans-Christian Egtvedt }
685f565502SHans-Christian Egtvedt 
695298cc4cSBill Pemberton static int gpio_mouse_probe(struct platform_device *pdev)
705f565502SHans-Christian Egtvedt {
71c5053e69SLinus Walleij 	struct device *dev = &pdev->dev;
7234cf5a1cSLinus Walleij 	struct gpio_mouse *gmouse;
735f565502SHans-Christian Egtvedt 	struct input_dev *input;
743df955f8SDmitry Torokhov 	int error;
755f565502SHans-Christian Egtvedt 
7634cf5a1cSLinus Walleij 	gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL);
7734cf5a1cSLinus Walleij 	if (!gmouse)
78c5053e69SLinus Walleij 		return -ENOMEM;
795f565502SHans-Christian Egtvedt 
80836bd419SLinus Walleij 	/* Assign some default scanning time */
813df955f8SDmitry Torokhov 	error = device_property_read_u32(dev, "scan-interval-ms",
82836bd419SLinus Walleij 					 &gmouse->scan_ms);
833df955f8SDmitry Torokhov 	if (error || gmouse->scan_ms == 0) {
84836bd419SLinus Walleij 		dev_warn(dev, "invalid scan time, set to 50 ms\n");
85836bd419SLinus Walleij 		gmouse->scan_ms = 50;
865f565502SHans-Christian Egtvedt 	}
875f565502SHans-Christian Egtvedt 
88836bd419SLinus Walleij 	gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN);
89836bd419SLinus Walleij 	if (IS_ERR(gmouse->up))
90836bd419SLinus Walleij 		return PTR_ERR(gmouse->up);
91836bd419SLinus Walleij 	gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN);
92836bd419SLinus Walleij 	if (IS_ERR(gmouse->down))
93836bd419SLinus Walleij 		return PTR_ERR(gmouse->down);
94836bd419SLinus Walleij 	gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN);
95836bd419SLinus Walleij 	if (IS_ERR(gmouse->left))
96836bd419SLinus Walleij 		return PTR_ERR(gmouse->left);
97836bd419SLinus Walleij 	gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN);
98836bd419SLinus Walleij 	if (IS_ERR(gmouse->right))
99836bd419SLinus Walleij 		return PTR_ERR(gmouse->right);
1005f565502SHans-Christian Egtvedt 
101836bd419SLinus Walleij 	gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN);
102836bd419SLinus Walleij 	if (IS_ERR(gmouse->bleft))
103836bd419SLinus Walleij 		return PTR_ERR(gmouse->bleft);
104836bd419SLinus Walleij 	gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle",
105836bd419SLinus Walleij 						  GPIOD_IN);
106836bd419SLinus Walleij 	if (IS_ERR(gmouse->bmiddle))
107836bd419SLinus Walleij 		return PTR_ERR(gmouse->bmiddle);
108836bd419SLinus Walleij 	gmouse->bright = devm_gpiod_get_optional(dev, "button-right",
109836bd419SLinus Walleij 						 GPIOD_IN);
110836bd419SLinus Walleij 	if (IS_ERR(gmouse->bright))
111836bd419SLinus Walleij 		return PTR_ERR(gmouse->bright);
1125f565502SHans-Christian Egtvedt 
1133df955f8SDmitry Torokhov 	input = devm_input_allocate_device(dev);
1143df955f8SDmitry Torokhov 	if (!input)
115836bd419SLinus Walleij 		return -ENOMEM;
1165f565502SHans-Christian Egtvedt 
1175f565502SHans-Christian Egtvedt 	input->name = pdev->name;
1185f565502SHans-Christian Egtvedt 	input->id.bustype = BUS_HOST;
1193df955f8SDmitry Torokhov 
1203df955f8SDmitry Torokhov 	input_set_drvdata(input, gmouse);
1215f565502SHans-Christian Egtvedt 
1225f565502SHans-Christian Egtvedt 	input_set_capability(input, EV_REL, REL_X);
1235f565502SHans-Christian Egtvedt 	input_set_capability(input, EV_REL, REL_Y);
124836bd419SLinus Walleij 	if (gmouse->bleft)
1255f565502SHans-Christian Egtvedt 		input_set_capability(input, EV_KEY, BTN_LEFT);
126836bd419SLinus Walleij 	if (gmouse->bmiddle)
1275f565502SHans-Christian Egtvedt 		input_set_capability(input, EV_KEY, BTN_MIDDLE);
128836bd419SLinus Walleij 	if (gmouse->bright)
1295f565502SHans-Christian Egtvedt 		input_set_capability(input, EV_KEY, BTN_RIGHT);
1305f565502SHans-Christian Egtvedt 
1313df955f8SDmitry Torokhov 	error = input_setup_polling(input, gpio_mouse_scan);
1323df955f8SDmitry Torokhov 	if (error)
1333df955f8SDmitry Torokhov 		return error;
1343df955f8SDmitry Torokhov 
1353df955f8SDmitry Torokhov 	input_set_poll_interval(input, gmouse->scan_ms);
1363df955f8SDmitry Torokhov 
1373df955f8SDmitry Torokhov 	error = input_register_device(input);
1383df955f8SDmitry Torokhov 	if (error) {
139836bd419SLinus Walleij 		dev_err(dev, "could not register input device\n");
1403df955f8SDmitry Torokhov 		return error;
1415f565502SHans-Christian Egtvedt 	}
1425f565502SHans-Christian Egtvedt 
143836bd419SLinus Walleij 	dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n",
14434cf5a1cSLinus Walleij 		gmouse->scan_ms,
145836bd419SLinus Walleij 		gmouse->bleft ? "" : "left ",
146836bd419SLinus Walleij 		gmouse->bmiddle ? "" : "middle ",
147836bd419SLinus Walleij 		gmouse->bright ? "" : "right");
1485f565502SHans-Christian Egtvedt 
1495f565502SHans-Christian Egtvedt 	return 0;
1505f565502SHans-Christian Egtvedt }
1515f565502SHans-Christian Egtvedt 
152adb77b3eSLinus Walleij static const struct of_device_id gpio_mouse_of_match[] = {
153adb77b3eSLinus Walleij 	{ .compatible = "gpio-mouse", },
154adb77b3eSLinus Walleij 	{ },
155adb77b3eSLinus Walleij };
156adb77b3eSLinus Walleij MODULE_DEVICE_TABLE(of, gpio_mouse_of_match);
157adb77b3eSLinus Walleij 
1580de048abSRoel Kluin static struct platform_driver gpio_mouse_device_driver = {
159eeafa5efSSaeed Bishara 	.probe		= gpio_mouse_probe,
1605f565502SHans-Christian Egtvedt 	.driver		= {
1615f565502SHans-Christian Egtvedt 		.name	= "gpio_mouse",
162adb77b3eSLinus Walleij 		.of_match_table = gpio_mouse_of_match,
1635f565502SHans-Christian Egtvedt 	}
1645f565502SHans-Christian Egtvedt };
1654fcdeac5SJJ Ding module_platform_driver(gpio_mouse_device_driver);
1665f565502SHans-Christian Egtvedt 
1677c409522SHans-Christian Egtvedt MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
1685f565502SHans-Christian Egtvedt MODULE_DESCRIPTION("GPIO mouse driver");
1695f565502SHans-Christian Egtvedt MODULE_LICENSE("GPL");
170eeafa5efSSaeed Bishara MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */
171