diff options
Diffstat (limited to 'drivers/input/touchscreen/panjit_i2c.c')
-rw-r--r-- | drivers/input/touchscreen/panjit_i2c.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/panjit_i2c.c b/drivers/input/touchscreen/panjit_i2c.c new file mode 100644 index 00000000000..4fccebc52ea --- /dev/null +++ b/drivers/input/touchscreen/panjit_i2c.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /* | ||
2 | * drivers/input/touchscreen/panjit_i2c.c | ||
3 | * | ||
4 | * Touchscreen class input driver for Panjit touch panel using I2C bus | ||
5 | * | ||
6 | * Copyright (c) 2010, NVIDIA Corporation. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/device.h> | ||
25 | #include <linux/input.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/earlysuspend.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/i2c/panjit_ts.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/gpio.h> | ||
32 | #include <linux/slab.h> | ||
33 | |||
34 | #define CSR 0x00 | ||
35 | #define CSR_SCAN_EN (1 << 3) | ||
36 | #define CSR_SLEEP_EN (1 << 7) | ||
37 | #define C_FLAG 0x01 | ||
38 | #define X1_H 0x03 | ||
39 | |||
40 | #define DRIVER_NAME "panjit_touch" | ||
41 | |||
42 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
43 | static void pj_early_suspend(struct early_suspend *h); | ||
44 | static void pj_late_resume(struct early_suspend *h); | ||
45 | #endif | ||
46 | |||
47 | struct pj_data { | ||
48 | struct input_dev *input_dev; | ||
49 | struct i2c_client *client; | ||
50 | int gpio_reset; | ||
51 | struct early_suspend early_suspend; | ||
52 | }; | ||
53 | |||
54 | struct pj_event { | ||
55 | __be16 coord[2][2]; | ||
56 | __u8 fingers; | ||
57 | __u8 gesture; | ||
58 | }; | ||
59 | |||
60 | union pj_buff { | ||
61 | struct pj_event data; | ||
62 | unsigned char buff[sizeof(struct pj_data)]; | ||
63 | }; | ||
64 | |||
65 | static void pj_reset(struct pj_data *touch) | ||
66 | { | ||
67 | if (touch->gpio_reset < 0) | ||
68 | return; | ||
69 | |||
70 | gpio_set_value(touch->gpio_reset, 1); | ||
71 | msleep(50); | ||
72 | gpio_set_value(touch->gpio_reset, 0); | ||
73 | msleep(50); | ||
74 | } | ||
75 | |||
76 | static irqreturn_t pj_irq(int irq, void *dev_id) | ||
77 | { | ||
78 | struct pj_data *touch = dev_id; | ||
79 | struct i2c_client *client = touch->client; | ||
80 | union pj_buff event; | ||
81 | int ret, i; | ||
82 | |||
83 | ret = i2c_smbus_read_i2c_block_data(client, X1_H, | ||
84 | sizeof(event.buff), event.buff); | ||
85 | if (WARN_ON(ret < 0)) { | ||
86 | dev_err(&client->dev, "error %d reading event data\n", ret); | ||
87 | return IRQ_NONE; | ||
88 | } | ||
89 | ret = i2c_smbus_write_byte_data(client, C_FLAG, 0); | ||
90 | if (WARN_ON(ret < 0)) { | ||
91 | dev_err(&client->dev, "error %d clearing interrupt\n", ret); | ||
92 | return IRQ_NONE; | ||
93 | } | ||
94 | |||
95 | input_report_key(touch->input_dev, BTN_TOUCH, | ||
96 | (event.data.fingers == 1 || event.data.fingers == 2)); | ||
97 | input_report_key(touch->input_dev, BTN_2, (event.data.fingers == 2)); | ||
98 | |||
99 | if (!event.data.fingers || (event.data.fingers > 2)) | ||
100 | goto out; | ||
101 | |||
102 | for (i = 0; i < event.data.fingers; i++) { | ||
103 | input_report_abs(touch->input_dev, ABS_MT_POSITION_X, | ||
104 | __be16_to_cpu(event.data.coord[i][0])); | ||
105 | input_report_abs(touch->input_dev, ABS_MT_POSITION_Y, | ||
106 | __be16_to_cpu(event.data.coord[i][1])); | ||
107 | input_report_abs(touch->input_dev, ABS_MT_TRACKING_ID, i + 1); | ||
108 | input_mt_sync(touch->input_dev); | ||
109 | } | ||
110 | |||
111 | out: | ||
112 | input_sync(touch->input_dev); | ||
113 | return IRQ_HANDLED; | ||
114 | } | ||
115 | |||
116 | static int pj_probe(struct i2c_client *client, | ||
117 | const struct i2c_device_id *id) | ||
118 | { | ||
119 | struct panjit_i2c_ts_platform_data *pdata = client->dev.platform_data; | ||
120 | struct pj_data *touch = NULL; | ||
121 | struct input_dev *input_dev = NULL; | ||
122 | int ret = 0; | ||
123 | |||
124 | touch = kzalloc(sizeof(struct pj_data), GFP_KERNEL); | ||
125 | if (!touch) { | ||
126 | dev_err(&client->dev, "%s: no memory\n", __func__); | ||
127 | return -ENOMEM; | ||
128 | } | ||
129 | |||
130 | touch->gpio_reset = -EINVAL; | ||
131 | |||
132 | if (pdata) { | ||
133 | ret = gpio_request(pdata->gpio_reset, "panjit_reset"); | ||
134 | if (!ret) { | ||
135 | ret = gpio_direction_output(pdata->gpio_reset, 1); | ||
136 | if (ret < 0) | ||
137 | gpio_free(pdata->gpio_reset); | ||
138 | } | ||
139 | |||
140 | if (!ret) | ||
141 | touch->gpio_reset = pdata->gpio_reset; | ||
142 | else | ||
143 | dev_warn(&client->dev, "unable to configure GPIO\n"); | ||
144 | } | ||
145 | |||
146 | input_dev = input_allocate_device(); | ||
147 | if (!input_dev) { | ||
148 | dev_err(&client->dev, "%s: no memory\n", __func__); | ||
149 | kfree(touch); | ||
150 | return -ENOMEM; | ||
151 | } | ||
152 | |||
153 | touch->client = client; | ||
154 | i2c_set_clientdata(client, touch); | ||
155 | |||
156 | pj_reset(touch); | ||
157 | |||
158 | /* clear interrupt */ | ||
159 | ret = i2c_smbus_write_byte_data(touch->client, C_FLAG, 0); | ||
160 | if (ret < 0) { | ||
161 | dev_err(&client->dev, "%s: clear interrupt failed\n", | ||
162 | __func__); | ||
163 | goto fail_i2c_or_register; | ||
164 | } | ||
165 | |||
166 | /* enable scanning */ | ||
167 | ret = i2c_smbus_write_byte_data(touch->client, CSR, CSR_SCAN_EN); | ||
168 | if (ret < 0) { | ||
169 | dev_err(&client->dev, "%s: enable interrupt failed\n", | ||
170 | __func__); | ||
171 | goto fail_i2c_or_register; | ||
172 | } | ||
173 | |||
174 | touch->input_dev = input_dev; | ||
175 | touch->input_dev->name = DRIVER_NAME; | ||
176 | |||
177 | set_bit(EV_SYN, touch->input_dev->evbit); | ||
178 | set_bit(EV_KEY, touch->input_dev->evbit); | ||
179 | set_bit(EV_ABS, touch->input_dev->evbit); | ||
180 | set_bit(BTN_TOUCH, touch->input_dev->keybit); | ||
181 | set_bit(BTN_2, touch->input_dev->keybit); | ||
182 | |||
183 | /* expose multi-touch capabilities */ | ||
184 | set_bit(ABS_MT_POSITION_X, touch->input_dev->keybit); | ||
185 | set_bit(ABS_MT_POSITION_Y, touch->input_dev->keybit); | ||
186 | set_bit(ABS_X, touch->input_dev->keybit); | ||
187 | set_bit(ABS_Y, touch->input_dev->keybit); | ||
188 | |||
189 | /* all coordinates are reported in 0..4095 */ | ||
190 | input_set_abs_params(touch->input_dev, ABS_X, 0, 4095, 0, 0); | ||
191 | input_set_abs_params(touch->input_dev, ABS_Y, 0, 4095, 0, 0); | ||
192 | input_set_abs_params(touch->input_dev, ABS_HAT0X, 0, 4095, 0, 0); | ||
193 | input_set_abs_params(touch->input_dev, ABS_HAT0Y, 0, 4095, 0, 0); | ||
194 | input_set_abs_params(touch->input_dev, ABS_HAT1X, 0, 4095, 0, 0); | ||
195 | input_set_abs_params(touch->input_dev, ABS_HAT1Y, 0, 4095, 0, 0); | ||
196 | |||
197 | input_set_abs_params(touch->input_dev, ABS_MT_POSITION_X, 0, 4095, 0, 0); | ||
198 | input_set_abs_params(touch->input_dev, ABS_MT_POSITION_Y, 0, 4095, 0, 0); | ||
199 | input_set_abs_params(touch->input_dev, ABS_MT_TRACKING_ID, 0, 2, 1, 0); | ||
200 | |||
201 | ret = input_register_device(touch->input_dev); | ||
202 | if (ret) { | ||
203 | dev_err(&client->dev, "%s: input_register_device failed\n", | ||
204 | __func__); | ||
205 | goto fail_i2c_or_register; | ||
206 | } | ||
207 | |||
208 | /* get the irq */ | ||
209 | ret = request_threaded_irq(touch->client->irq, NULL, pj_irq, | ||
210 | IRQF_ONESHOT | IRQF_TRIGGER_LOW, | ||
211 | DRIVER_NAME, touch); | ||
212 | if (ret) { | ||
213 | dev_err(&client->dev, "%s: request_irq(%d) failed\n", | ||
214 | __func__, touch->client->irq); | ||
215 | goto fail_irq; | ||
216 | } | ||
217 | |||
218 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
219 | touch->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; | ||
220 | touch->early_suspend.suspend = pj_early_suspend; | ||
221 | touch->early_suspend.resume = pj_late_resume; | ||
222 | register_early_suspend(&touch->early_suspend); | ||
223 | #endif | ||
224 | dev_info(&client->dev, "%s: initialized\n", __func__); | ||
225 | return 0; | ||
226 | |||
227 | fail_irq: | ||
228 | input_unregister_device(touch->input_dev); | ||
229 | |||
230 | fail_i2c_or_register: | ||
231 | if (touch->gpio_reset >= 0) | ||
232 | gpio_free(touch->gpio_reset); | ||
233 | |||
234 | input_free_device(input_dev); | ||
235 | kfree(touch); | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | static int pj_suspend(struct i2c_client *client, pm_message_t state) | ||
240 | { | ||
241 | struct pj_data *touch = i2c_get_clientdata(client); | ||
242 | int ret; | ||
243 | |||
244 | if (WARN_ON(!touch)) | ||
245 | return -EINVAL; | ||
246 | |||
247 | disable_irq(client->irq); | ||
248 | |||
249 | /* disable scanning and enable deep sleep */ | ||
250 | ret = i2c_smbus_write_byte_data(client, CSR, CSR_SLEEP_EN); | ||
251 | if (ret < 0) { | ||
252 | dev_err(&client->dev, "%s: sleep enable fail\n", __func__); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int pj_resume(struct i2c_client *client) | ||
260 | { | ||
261 | struct pj_data *touch = i2c_get_clientdata(client); | ||
262 | int ret = 0; | ||
263 | |||
264 | if (WARN_ON(!touch)) | ||
265 | return -EINVAL; | ||
266 | |||
267 | pj_reset(touch); | ||
268 | |||
269 | /* enable scanning and disable deep sleep */ | ||
270 | ret = i2c_smbus_write_byte_data(client, C_FLAG, 0); | ||
271 | if (ret >= 0) | ||
272 | ret = i2c_smbus_write_byte_data(client, CSR, CSR_SCAN_EN); | ||
273 | if (ret < 0) { | ||
274 | dev_err(&client->dev, "%s: scan enable fail\n", __func__); | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | enable_irq(client->irq); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
284 | static void pj_early_suspend(struct early_suspend *es) | ||
285 | { | ||
286 | struct pj_data *touch; | ||
287 | touch = container_of(es, struct pj_data, early_suspend); | ||
288 | |||
289 | if (pj_suspend(touch->client, PMSG_SUSPEND) != 0) | ||
290 | dev_err(&touch->client->dev, "%s: failed\n", __func__); | ||
291 | } | ||
292 | |||
293 | static void pj_late_resume(struct early_suspend *es) | ||
294 | { | ||
295 | struct pj_data *touch; | ||
296 | touch = container_of(es, struct pj_data, early_suspend); | ||
297 | |||
298 | if (pj_resume(touch->client) != 0) | ||
299 | dev_err(&touch->client->dev, "%s: failed\n", __func__); | ||
300 | } | ||
301 | #endif | ||
302 | |||
303 | static int pj_remove(struct i2c_client *client) | ||
304 | { | ||
305 | struct pj_data *touch = i2c_get_clientdata(client); | ||
306 | |||
307 | if (!touch) | ||
308 | return -EINVAL; | ||
309 | |||
310 | #ifdef CONFIG_HAS_EARLYSUSPEND | ||
311 | unregister_early_suspend(&touch->early_suspend); | ||
312 | #endif | ||
313 | free_irq(touch->client->irq, touch); | ||
314 | if (touch->gpio_reset >= 0) | ||
315 | gpio_free(touch->gpio_reset); | ||
316 | input_unregister_device(touch->input_dev); | ||
317 | input_free_device(touch->input_dev); | ||
318 | kfree(touch); | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static const struct i2c_device_id panjit_ts_id[] = { | ||
323 | { DRIVER_NAME, 0 }, | ||
324 | { } | ||
325 | }; | ||
326 | |||
327 | static struct i2c_driver panjit_driver = { | ||
328 | .probe = pj_probe, | ||
329 | .remove = pj_remove, | ||
330 | #ifndef CONFIG_HAS_EARLYSUSPEND | ||
331 | .suspend = pj_suspend, | ||
332 | .resume = pj_resume, | ||
333 | #endif | ||
334 | .id_table = panjit_ts_id, | ||
335 | .driver = { | ||
336 | .name = DRIVER_NAME, | ||
337 | }, | ||
338 | }; | ||
339 | |||
340 | static int __devinit panjit_init(void) | ||
341 | { | ||
342 | int e; | ||
343 | |||
344 | e = i2c_add_driver(&panjit_driver); | ||
345 | if (e != 0) { | ||
346 | pr_err("%s: failed to register with I2C bus with " | ||
347 | "error: 0x%x\n", __func__, e); | ||
348 | } | ||
349 | return e; | ||
350 | } | ||
351 | |||
352 | static void __exit panjit_exit(void) | ||
353 | { | ||
354 | i2c_del_driver(&panjit_driver); | ||
355 | } | ||
356 | |||
357 | module_init(panjit_init); | ||
358 | module_exit(panjit_exit); | ||
359 | |||
360 | MODULE_LICENSE("GPL"); | ||
361 | MODULE_DESCRIPTION("Panjit I2C touch driver"); | ||