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> | |
11 | #include <linux/input-polldev.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 HCE |
45 | */ |
46 | static void gpio_mouse_scan(struct input_polled_dev *dev) | |
47 | { | |
34cf5a1c | 48 | struct gpio_mouse *gpio = dev->private; |
5f565502 HCE |
49 | struct input_dev *input = dev->input; |
50 | int x, y; | |
51 | ||
836bd419 | 52 | if (gpio->bleft) |
5f565502 | 53 | input_report_key(input, BTN_LEFT, |
836bd419 LW |
54 | gpiod_get_value(gpio->bleft)); |
55 | if (gpio->bmiddle) | |
5f565502 | 56 | input_report_key(input, BTN_MIDDLE, |
836bd419 LW |
57 | gpiod_get_value(gpio->bmiddle)); |
58 | if (gpio->bright) | |
5f565502 | 59 | input_report_key(input, BTN_RIGHT, |
836bd419 | 60 | gpiod_get_value(gpio->bright)); |
5f565502 | 61 | |
836bd419 LW |
62 | x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left); |
63 | y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up); | |
5f565502 HCE |
64 | |
65 | input_report_rel(input, REL_X, x); | |
66 | input_report_rel(input, REL_Y, y); | |
67 | input_sync(input); | |
68 | } | |
69 | ||
5298cc4c | 70 | static int gpio_mouse_probe(struct platform_device *pdev) |
5f565502 | 71 | { |
c5053e69 | 72 | struct device *dev = &pdev->dev; |
34cf5a1c | 73 | struct gpio_mouse *gmouse; |
5f565502 HCE |
74 | struct input_polled_dev *input_poll; |
75 | struct input_dev *input; | |
836bd419 | 76 | int ret; |
5f565502 | 77 | |
34cf5a1c LW |
78 | gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL); |
79 | if (!gmouse) | |
c5053e69 | 80 | return -ENOMEM; |
5f565502 | 81 | |
836bd419 LW |
82 | /* Assign some default scanning time */ |
83 | ret = device_property_read_u32(dev, "scan-interval-ms", | |
84 | &gmouse->scan_ms); | |
85 | if (ret || gmouse->scan_ms == 0) { | |
86 | dev_warn(dev, "invalid scan time, set to 50 ms\n"); | |
87 | gmouse->scan_ms = 50; | |
5f565502 HCE |
88 | } |
89 | ||
836bd419 LW |
90 | gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN); |
91 | if (IS_ERR(gmouse->up)) | |
92 | return PTR_ERR(gmouse->up); | |
93 | gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN); | |
94 | if (IS_ERR(gmouse->down)) | |
95 | return PTR_ERR(gmouse->down); | |
96 | gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN); | |
97 | if (IS_ERR(gmouse->left)) | |
98 | return PTR_ERR(gmouse->left); | |
99 | gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN); | |
100 | if (IS_ERR(gmouse->right)) | |
101 | return PTR_ERR(gmouse->right); | |
102 | ||
103 | gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN); | |
104 | if (IS_ERR(gmouse->bleft)) | |
105 | return PTR_ERR(gmouse->bleft); | |
106 | gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle", | |
107 | GPIOD_IN); | |
108 | if (IS_ERR(gmouse->bmiddle)) | |
109 | return PTR_ERR(gmouse->bmiddle); | |
110 | gmouse->bright = devm_gpiod_get_optional(dev, "button-right", | |
111 | GPIOD_IN); | |
112 | if (IS_ERR(gmouse->bright)) | |
113 | return PTR_ERR(gmouse->bright); | |
114 | ||
115 | input_poll = devm_input_allocate_polled_device(dev); | |
5f565502 | 116 | if (!input_poll) { |
836bd419 LW |
117 | dev_err(dev, "not enough memory for input device\n"); |
118 | return -ENOMEM; | |
5f565502 HCE |
119 | } |
120 | ||
121 | platform_set_drvdata(pdev, input_poll); | |
122 | ||
123 | /* set input-polldev handlers */ | |
34cf5a1c | 124 | input_poll->private = gmouse; |
5f565502 | 125 | input_poll->poll = gpio_mouse_scan; |
34cf5a1c | 126 | input_poll->poll_interval = gmouse->scan_ms; |
5f565502 HCE |
127 | |
128 | input = input_poll->input; | |
129 | input->name = pdev->name; | |
130 | input->id.bustype = BUS_HOST; | |
131 | input->dev.parent = &pdev->dev; | |
132 | ||
133 | input_set_capability(input, EV_REL, REL_X); | |
134 | input_set_capability(input, EV_REL, REL_Y); | |
836bd419 | 135 | if (gmouse->bleft) |
5f565502 | 136 | input_set_capability(input, EV_KEY, BTN_LEFT); |
836bd419 | 137 | if (gmouse->bmiddle) |
5f565502 | 138 | input_set_capability(input, EV_KEY, BTN_MIDDLE); |
836bd419 | 139 | if (gmouse->bright) |
5f565502 HCE |
140 | input_set_capability(input, EV_KEY, BTN_RIGHT); |
141 | ||
836bd419 LW |
142 | ret = input_register_polled_device(input_poll); |
143 | if (ret) { | |
144 | dev_err(dev, "could not register input device\n"); | |
145 | return ret; | |
5f565502 | 146 | } |
5f565502 | 147 | |
836bd419 LW |
148 | dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n", |
149 | gmouse->scan_ms, | |
150 | gmouse->bleft ? "" : "left ", | |
151 | gmouse->bmiddle ? "" : "middle ", | |
152 | gmouse->bright ? "" : "right"); | |
5f565502 | 153 | |
5f565502 HCE |
154 | return 0; |
155 | } | |
156 | ||
adb77b3e LW |
157 | static const struct of_device_id gpio_mouse_of_match[] = { |
158 | { .compatible = "gpio-mouse", }, | |
159 | { }, | |
160 | }; | |
161 | MODULE_DEVICE_TABLE(of, gpio_mouse_of_match); | |
162 | ||
0de048ab | 163 | static struct platform_driver gpio_mouse_device_driver = { |
eeafa5ef | 164 | .probe = gpio_mouse_probe, |
5f565502 HCE |
165 | .driver = { |
166 | .name = "gpio_mouse", | |
adb77b3e | 167 | .of_match_table = gpio_mouse_of_match, |
5f565502 HCE |
168 | } |
169 | }; | |
4fcdeac5 | 170 | module_platform_driver(gpio_mouse_device_driver); |
5f565502 | 171 | |
7c409522 | 172 | MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); |
5f565502 HCE |
173 | MODULE_DESCRIPTION("GPIO mouse driver"); |
174 | MODULE_LICENSE("GPL"); | |
eeafa5ef | 175 | MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */ |