1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * h3600 atmel micro companion support, touchscreen subdevice |
5 | * Author : Alessandro Gardich <gremlin@gremlin.it> |
6 | * Author : Dmitry Artamonow <mad_soft@inbox.ru> |
7 | * Author : Linus Walleij <linus.walleij@linaro.org> |
8 | */ |
9 | |
10 | #include <asm/byteorder.h> |
11 | #include <linux/module.h> |
12 | #include <linux/init.h> |
13 | #include <linux/interrupt.h> |
14 | #include <linux/pm.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/device.h> |
17 | #include <linux/input.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/mfd/ipaq-micro.h> |
21 | |
22 | struct touchscreen_data { |
23 | struct input_dev *input; |
24 | struct ipaq_micro *micro; |
25 | }; |
26 | |
27 | static void micro_ts_receive(void *data, int len, unsigned char *msg) |
28 | { |
29 | struct touchscreen_data *ts = data; |
30 | |
31 | if (len == 4) { |
32 | input_report_abs(dev: ts->input, ABS_X, |
33 | be16_to_cpup(p: (__be16 *) &msg[2])); |
34 | input_report_abs(dev: ts->input, ABS_Y, |
35 | be16_to_cpup(p: (__be16 *) &msg[0])); |
36 | input_report_key(dev: ts->input, BTN_TOUCH, value: 1); |
37 | input_sync(dev: ts->input); |
38 | } else if (len == 0) { |
39 | input_report_abs(dev: ts->input, ABS_X, value: 0); |
40 | input_report_abs(dev: ts->input, ABS_Y, value: 0); |
41 | input_report_key(dev: ts->input, BTN_TOUCH, value: 0); |
42 | input_sync(dev: ts->input); |
43 | } |
44 | } |
45 | |
46 | static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) |
47 | { |
48 | struct ipaq_micro *micro = ts->micro; |
49 | |
50 | spin_lock_irq(lock: µ->lock); |
51 | |
52 | if (enable) { |
53 | micro->ts = micro_ts_receive; |
54 | micro->ts_data = ts; |
55 | } else { |
56 | micro->ts = NULL; |
57 | micro->ts_data = NULL; |
58 | } |
59 | |
60 | spin_unlock_irq(lock: &ts->micro->lock); |
61 | } |
62 | |
63 | static int micro_ts_open(struct input_dev *input) |
64 | { |
65 | struct touchscreen_data *ts = input_get_drvdata(dev: input); |
66 | |
67 | micro_ts_toggle_receive(ts, enable: true); |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | static void micro_ts_close(struct input_dev *input) |
73 | { |
74 | struct touchscreen_data *ts = input_get_drvdata(dev: input); |
75 | |
76 | micro_ts_toggle_receive(ts, enable: false); |
77 | } |
78 | |
79 | static int micro_ts_probe(struct platform_device *pdev) |
80 | { |
81 | struct ipaq_micro *micro = dev_get_drvdata(dev: pdev->dev.parent); |
82 | struct touchscreen_data *ts; |
83 | int error; |
84 | |
85 | ts = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ts), GFP_KERNEL); |
86 | if (!ts) |
87 | return -ENOMEM; |
88 | |
89 | ts->micro = micro; |
90 | |
91 | ts->input = devm_input_allocate_device(&pdev->dev); |
92 | if (!ts->input) { |
93 | dev_err(&pdev->dev, "failed to allocate input device\n" ); |
94 | return -ENOMEM; |
95 | } |
96 | |
97 | ts->input->name = "ipaq micro ts" ; |
98 | ts->input->open = micro_ts_open; |
99 | ts->input->close = micro_ts_close; |
100 | |
101 | input_set_drvdata(dev: ts->input, data: ts); |
102 | |
103 | input_set_capability(dev: ts->input, EV_KEY, BTN_TOUCH); |
104 | input_set_capability(dev: ts->input, EV_ABS, ABS_X); |
105 | input_set_capability(dev: ts->input, EV_ABS, ABS_Y); |
106 | input_set_abs_params(dev: ts->input, ABS_X, min: 0, max: 1023, fuzz: 0, flat: 0); |
107 | input_set_abs_params(dev: ts->input, ABS_Y, min: 0, max: 1023, fuzz: 0, flat: 0); |
108 | |
109 | error = input_register_device(ts->input); |
110 | if (error) { |
111 | dev_err(&pdev->dev, "error registering touch input\n" ); |
112 | return error; |
113 | } |
114 | |
115 | platform_set_drvdata(pdev, data: ts); |
116 | |
117 | dev_info(&pdev->dev, "iPAQ micro touchscreen\n" ); |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static int micro_ts_suspend(struct device *dev) |
123 | { |
124 | struct touchscreen_data *ts = dev_get_drvdata(dev); |
125 | |
126 | micro_ts_toggle_receive(ts, enable: false); |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | static int micro_ts_resume(struct device *dev) |
132 | { |
133 | struct touchscreen_data *ts = dev_get_drvdata(dev); |
134 | struct input_dev *input = ts->input; |
135 | |
136 | mutex_lock(&input->mutex); |
137 | |
138 | if (input_device_enabled(dev: input)) |
139 | micro_ts_toggle_receive(ts, enable: true); |
140 | |
141 | mutex_unlock(lock: &input->mutex); |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | static DEFINE_SIMPLE_DEV_PM_OPS(micro_ts_dev_pm_ops, |
147 | micro_ts_suspend, micro_ts_resume); |
148 | |
149 | static struct platform_driver micro_ts_device_driver = { |
150 | .driver = { |
151 | .name = "ipaq-micro-ts" , |
152 | .pm = pm_sleep_ptr(µ_ts_dev_pm_ops), |
153 | }, |
154 | .probe = micro_ts_probe, |
155 | }; |
156 | module_platform_driver(micro_ts_device_driver); |
157 | |
158 | MODULE_LICENSE("GPL" ); |
159 | MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen" ); |
160 | MODULE_ALIAS("platform:ipaq-micro-ts" ); |
161 | |