Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5f565502 HCE |
2 | /* |
3 | * Driver for simulating a mouse on GPIO lines. | |
4 | * | |
5 | * Copyright (C) 2007 Atmel Corporation | |
836bd419 | 6 | * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> |
5f565502 HCE |
7 | */ |
8 | ||
5f565502 HCE |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> | |
3df955f8 | 11 | #include <linux/input.h> |
836bd419 LW |
12 | #include <linux/gpio/consumer.h> |
13 | #include <linux/property.h> | |
adb77b3e | 14 | #include <linux/of.h> |
c5053e69 LW |
15 | |
16 | /** | |
34cf5a1c | 17 | * struct gpio_mouse |
836bd419 | 18 | * @scan_ms: the scan interval in milliseconds. |
c5053e69 LW |
19 | * @up: GPIO line for up value. |
20 | * @down: GPIO line for down value. | |
21 | * @left: GPIO line for left value. | |
22 | * @right: GPIO line for right value. | |
23 | * @bleft: GPIO line for left button. | |
24 | * @bmiddle: GPIO line for middle button. | |
25 | * @bright: GPIO line for right button. | |
c5053e69 LW |
26 | * |
27 | * This struct must be added to the platform_device in the board code. | |
28 | * It is used by the gpio_mouse driver to setup GPIO lines and to | |
29 | * calculate mouse movement. | |
30 | */ | |
34cf5a1c | 31 | struct gpio_mouse { |
836bd419 LW |
32 | u32 scan_ms; |
33 | struct gpio_desc *up; | |
34 | struct gpio_desc *down; | |
35 | struct gpio_desc *left; | |
36 | struct gpio_desc *right; | |
37 | struct gpio_desc *bleft; | |
38 | struct gpio_desc *bmiddle; | |
39 | struct gpio_desc *bright; | |
c5053e69 | 40 | }; |
5f565502 HCE |
41 | |
42 | /* | |
43 | * Timer function which is run every scan_ms ms when the device is opened. | |
3a070ad1 | 44 | * The dev input variable is set to the the input_dev pointer. |
5f565502 | 45 | */ |
3df955f8 | 46 | static void gpio_mouse_scan(struct input_dev *input) |
5f565502 | 47 | { |
3df955f8 | 48 | struct gpio_mouse *gpio = input_get_drvdata(input); |
5f565502 HCE |
49 | int x, y; |
50 | ||
836bd419 | 51 | if (gpio->bleft) |
5f565502 | 52 | input_report_key(input, BTN_LEFT, |
836bd419 LW |
53 | gpiod_get_value(gpio->bleft)); |
54 | if (gpio->bmiddle) | |
5f565502 | 55 | input_report_key(input, BTN_MIDDLE, |
836bd419 LW |
56 | gpiod_get_value(gpio->bmiddle)); |
57 | if (gpio->bright) | |
5f565502 | 58 | input_report_key(input, BTN_RIGHT, |
836bd419 | 59 | gpiod_get_value(gpio->bright)); |
5f565502 | 60 | |
836bd419 LW |
61 | x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left); |
62 | y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up); | |
5f565502 HCE |
63 | |
64 | input_report_rel(input, REL_X, x); | |
65 | input_report_rel(input, REL_Y, y); | |
66 | input_sync(input); | |
67 | } | |
68 | ||
5298cc4c | 69 | static int gpio_mouse_probe(struct platform_device *pdev) |
5f565502 | 70 | { |
c5053e69 | 71 | struct device *dev = &pdev->dev; |
34cf5a1c | 72 | struct gpio_mouse *gmouse; |
5f565502 | 73 | struct input_dev *input; |
3df955f8 | 74 | int error; |
5f565502 | 75 | |
34cf5a1c LW |
76 | gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL); |
77 | if (!gmouse) | |
c5053e69 | 78 | return -ENOMEM; |
5f565502 | 79 | |
836bd419 | 80 | /* Assign some default scanning time */ |
3df955f8 DT |
81 | error = device_property_read_u32(dev, "scan-interval-ms", |
82 | &gmouse->scan_ms); | |
83 | if (error || gmouse->scan_ms == 0) { | |
836bd419 LW |
84 | dev_warn(dev, "invalid scan time, set to 50 ms\n"); |
85 | gmouse->scan_ms = 50; | |
5f565502 HCE |
86 | } |
87 | ||
836bd419 LW |
88 | gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN); |
89 | if (IS_ERR(gmouse->up)) | |
90 | return PTR_ERR(gmouse->up); | |
91 | gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN); | |
92 | if (IS_ERR(gmouse->down)) | |
93 | return PTR_ERR(gmouse->down); | |
94 | gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN); | |
95 | if (IS_ERR(gmouse->left)) | |
96 | return PTR_ERR(gmouse->left); | |
97 | gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN); | |
98 | if (IS_ERR(gmouse->right)) | |
99 | return PTR_ERR(gmouse->right); | |
100 | ||
101 | gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN); | |
102 | if (IS_ERR(gmouse->bleft)) | |
103 | return PTR_ERR(gmouse->bleft); | |
104 | gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle", | |
105 | GPIOD_IN); | |
106 | if (IS_ERR(gmouse->bmiddle)) | |
107 | return PTR_ERR(gmouse->bmiddle); | |
108 | gmouse->bright = devm_gpiod_get_optional(dev, "button-right", | |
109 | GPIOD_IN); | |
110 | if (IS_ERR(gmouse->bright)) | |
111 | return PTR_ERR(gmouse->bright); | |
112 | ||
3df955f8 DT |
113 | input = devm_input_allocate_device(dev); |
114 | if (!input) | |
836bd419 | 115 | return -ENOMEM; |
5f565502 | 116 | |
5f565502 HCE |
117 | input->name = pdev->name; |
118 | input->id.bustype = BUS_HOST; | |
3df955f8 DT |
119 | |
120 | input_set_drvdata(input, gmouse); | |
5f565502 HCE |
121 | |
122 | input_set_capability(input, EV_REL, REL_X); | |
123 | input_set_capability(input, EV_REL, REL_Y); | |
836bd419 | 124 | if (gmouse->bleft) |
5f565502 | 125 | input_set_capability(input, EV_KEY, BTN_LEFT); |
836bd419 | 126 | if (gmouse->bmiddle) |
5f565502 | 127 | input_set_capability(input, EV_KEY, BTN_MIDDLE); |
836bd419 | 128 | if (gmouse->bright) |
5f565502 HCE |
129 | input_set_capability(input, EV_KEY, BTN_RIGHT); |
130 | ||
3df955f8 DT |
131 | error = input_setup_polling(input, gpio_mouse_scan); |
132 | if (error) | |
133 | return error; | |
134 | ||
135 | input_set_poll_interval(input, gmouse->scan_ms); | |
136 | ||
137 | error = input_register_device(input); | |
138 | if (error) { | |
836bd419 | 139 | dev_err(dev, "could not register input device\n"); |
3df955f8 | 140 | return error; |
5f565502 | 141 | } |
5f565502 | 142 | |
836bd419 LW |
143 | dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n", |
144 | gmouse->scan_ms, | |
145 | gmouse->bleft ? "" : "left ", | |
146 | gmouse->bmiddle ? "" : "middle ", | |
147 | gmouse->bright ? "" : "right"); | |
5f565502 | 148 | |
5f565502 HCE |
149 | return 0; |
150 | } | |
151 | ||
adb77b3e LW |
152 | static const struct of_device_id gpio_mouse_of_match[] = { |
153 | { .compatible = "gpio-mouse", }, | |
154 | { }, | |
155 | }; | |
156 | MODULE_DEVICE_TABLE(of, gpio_mouse_of_match); | |
157 | ||
0de048ab | 158 | static struct platform_driver gpio_mouse_device_driver = { |
eeafa5ef | 159 | .probe = gpio_mouse_probe, |
5f565502 HCE |
160 | .driver = { |
161 | .name = "gpio_mouse", | |
adb77b3e | 162 | .of_match_table = gpio_mouse_of_match, |
5f565502 HCE |
163 | } |
164 | }; | |
4fcdeac5 | 165 | module_platform_driver(gpio_mouse_device_driver); |
5f565502 | 166 | |
7c409522 | 167 | MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); |
5f565502 HCE |
168 | MODULE_DESCRIPTION("GPIO mouse driver"); |
169 | MODULE_LICENSE("GPL"); | |
eeafa5ef | 170 | MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */ |