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