blob: f48374eb30e17b28462f89b6de676d3f95c6a046 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* drivers/input/touchscreen/cy8c_tmg_ts.c
2 *
3 * Copyright (C) 2007-2008 HTC Corporation.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/cy8c_tmg_ts.h>
17#include <linux/delay.h>
18#include <linux/earlysuspend.h>
19#include <linux/hrtimer.h>
20#include <linux/i2c.h>
21#include <linux/input.h>
22#include <linux/interrupt.h>
23#include <linux/module.h>
24#include <linux/platform_device.h>
25
26#define CY8C_REG_START_NEW_SCAN 0x0F
27#define CY8C_REG_INTR_STATUS 0x3C
28#define CY8C_REG_VERSION 0x3E
29
30struct cy8c_ts_data {
31 struct i2c_client *client;
32 struct input_dev *input_dev;
33 int use_irq;
34 struct hrtimer timer;
35 struct work_struct work;
36 uint16_t version;
37 int (*power) (int on);
38 struct early_suspend early_suspend;
39};
40
41struct workqueue_struct *cypress_touch_wq;
42
43#ifdef CONFIG_HAS_EARLYSUSPEND
44static void cy8c_ts_early_suspend(struct early_suspend *h);
45static void cy8c_ts_late_resume(struct early_suspend *h);
46#endif
47
48uint16_t sample_count, X_mean, Y_mean, first_touch;
49
50static s32 cy8c_read_word_data(struct i2c_client *client,
51 u8 command, uint16_t * data)
52{
53 s32 ret = i2c_smbus_read_word_data(client, command);
54 if (ret != -1) {
55 *data = (u16) ((ret << 8) | (ret >> 8));
56 }
57 return ret;
58}
59
60static int cy8c_init_panel(struct cy8c_ts_data *ts)
61{
62 int ret;
63 sample_count = X_mean = Y_mean = first_touch = 0;
64
65 /* clean intr busy */
66 ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS,
67 0x00);
68 if (ret < 0) {
69 dev_err(&ts->client->dev,
70 "cy8c_init_panel failed for clean intr busy\n");
71 goto exit;
72 }
73
74 /* start new scan */
75 ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_START_NEW_SCAN,
76 0x01);
77 if (ret < 0) {
78 dev_err(&ts->client->dev,
79 "cy8c_init_panel failed for start new scan\n");
80 goto exit;
81 }
82
83exit:
84 return ret;
85}
86
87static void cy8c_ts_reset(struct i2c_client *client)
88{
89 struct cy8c_ts_data *ts = i2c_get_clientdata(client);
90
91 if (ts->power) {
92 ts->power(0);
93 msleep(10);
94 ts->power(1);
95 msleep(10);
96 }
97
98 cy8c_init_panel(ts);
99}
100
101static void cy8c_ts_work_func(struct work_struct *work)
102{
103 struct cy8c_ts_data *ts = container_of(work, struct cy8c_ts_data, work);
104 uint16_t x1, y1, x2, y2;
105 uint8_t is_touch, start_reg, force, area, finger2_pressed;
106 uint8_t buf[11];
107 struct i2c_msg msg[2];
108 int ret = 0;
109
110 x2 = y2 = 0;
111
112 /*printk("%s: enter\n",__func__);*/
113 is_touch = i2c_smbus_read_byte_data(ts->client, 0x20);
114 dev_dbg(&ts->client->dev, "fIsTouch %d,\n", is_touch);
115 if (is_touch < 0 || is_touch > 3) {
116 pr_err("%s: invalid is_touch = %d\n", __func__, is_touch);
117 cy8c_ts_reset(ts->client);
118 msleep(10);
119 goto done;
120 }
121
122 msg[0].addr = ts->client->addr;
123 msg[0].flags = 0;
124 msg[0].len = 1;
125 start_reg = 0x16;
126 msg[0].buf = &start_reg;
127
128 msg[1].addr = ts->client->addr;
129 msg[1].flags = I2C_M_RD;
130 msg[1].len = sizeof(buf);
131 msg[1].buf = buf;
132
133 ret = i2c_transfer(ts->client->adapter, msg, 2);
134 if (ret < 0)
135 goto done;
136
137 /* parse data */
138 force = buf[0];
139 area = buf[1];
140 x1 = (buf[2] << 8) | buf[3];
141 y1 = (buf[6] << 8) | buf[7];
142 is_touch = buf[10];
143
144 if (is_touch == 2) {
145 x2 = (buf[4] << 8) | buf[5];
146 y2 = (buf[8] << 8) | buf[9];
147 finger2_pressed = 1;
148 }
149
150 dev_dbg(&ts->client->dev,
151 "bFingerForce %d, bFingerArea %d \n", force, area);
152 dev_dbg(&ts->client->dev, "x1: %d, y1: %d \n", x1, y1);
153 if (finger2_pressed)
154 dev_dbg(&ts->client->dev, "x2: %d, y2: %d \n", x2, y2);
155
156 /* drop the first one? */
157 if ((is_touch == 1) && (first_touch == 0)) {
158 first_touch = 1;
159 goto done;
160 }
161
162 if (!first_touch)
163 goto done;
164
165 if (is_touch == 2)
166 finger2_pressed = 1;
167
168 input_report_abs(ts->input_dev, ABS_X, x1);
169 input_report_abs(ts->input_dev, ABS_Y, y1);
170 input_report_abs(ts->input_dev, ABS_PRESSURE, force);
171 input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, area);
172 input_report_key(ts->input_dev, BTN_TOUCH, is_touch);
173 input_report_key(ts->input_dev, BTN_2, finger2_pressed);
174
175 if (finger2_pressed) {
176 input_report_abs(ts->input_dev, ABS_HAT0X, x2);
177 input_report_abs(ts->input_dev, ABS_HAT0Y, y2);
178 }
179 input_sync(ts->input_dev);
180
181done:
182 if (is_touch == 0)
183 first_touch = sample_count = 0;
184
185 /* prepare for next intr */
186 i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS, 0x00);
187 if (!ts->use_irq)
188 hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL);
189 else
190 enable_irq(ts->client->irq);
191}
192
193static enum hrtimer_restart cy8c_ts_timer_func(struct hrtimer *timer)
194{
195 struct cy8c_ts_data *ts;
196
197 ts = container_of(timer, struct cy8c_ts_data, timer);
198 queue_work(cypress_touch_wq, &ts->work);
199 return HRTIMER_NORESTART;
200}
201
202static irqreturn_t cy8c_ts_irq_handler(int irq, void *dev_id)
203{
204 struct cy8c_ts_data *ts = dev_id;
205
206 disable_irq_nosync(ts->client->irq);
207 queue_work(cypress_touch_wq, &ts->work);
208 return IRQ_HANDLED;
209}
210
211static int cy8c_ts_probe(struct i2c_client *client,
212 const struct i2c_device_id *id)
213{
214 struct cy8c_ts_data *ts;
215 struct cy8c_i2c_platform_data *pdata;
216 uint16_t panel_version;
217 int ret = 0;
218
219 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
220 dev_err(&client->dev, "need I2C_FUNC_I2C\n");
221 ret = -ENODEV;
222 goto err_check_functionality_failed;
223 }
224
225 ts = kzalloc(sizeof(struct cy8c_ts_data), GFP_KERNEL);
226 if (ts == NULL) {
227 dev_err(&client->dev, "allocate cy8c_ts_data failed\n");
228 ret = -ENOMEM;
229 goto err_alloc_data_failed;
230 }
231
232 INIT_WORK(&ts->work, cy8c_ts_work_func);
233 ts->client = client;
234 i2c_set_clientdata(client, ts);
235
236 pdata = client->dev.platform_data;
237 if (pdata) {
238 ts->version = pdata->version;
239 ts->power = pdata->power;
240 }
241
242 if (ts->power) {
243 ret = ts->power(1);
244 msleep(10);
245 if (ret < 0) {
246 dev_err(&client->dev, "power on failed\n");
247 goto err_power_failed;
248 }
249 }
250
251 ret = cy8c_read_word_data(ts->client, CY8C_REG_VERSION, &panel_version);
252 if (ret < 0) {
253 dev_err(&client->dev, "init panel failed\n");
254 goto err_detect_failed;
255 }
256 dev_info(&client->dev, "Panel Version %04X\n", panel_version);
257 if (pdata) {
258 while (pdata->version > panel_version) {
259 dev_info(&client->dev, "old tp detected, "
260 "panel version = %x\n", panel_version);
261 pdata++;
262 }
263 }
264
265 ret = cy8c_init_panel(ts);
266 if (ret < 0) {
267 dev_err(&client->dev, "init panel failed\n");
268 goto err_detect_failed;
269 }
270
271 ts->input_dev = input_allocate_device();
272 if (ts->input_dev == NULL) {
273 ret = -ENOMEM;
274 dev_err(&client->dev, "Failed to allocate input device\n");
275 goto err_input_dev_alloc_failed;
276 }
277 ts->input_dev->name = "cy8c-touchscreen";
278
279 set_bit(EV_SYN, ts->input_dev->evbit);
280 set_bit(EV_ABS, ts->input_dev->evbit);
281 set_bit(EV_KEY, ts->input_dev->evbit);
282 input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
283 input_set_capability(ts->input_dev, EV_KEY, BTN_2);
284
285 input_set_abs_params(ts->input_dev, ABS_X,
286 pdata->abs_x_min, pdata->abs_x_max, 5, 0);
287 input_set_abs_params(ts->input_dev, ABS_Y,
288 pdata->abs_y_min, pdata->abs_y_max, 5, 0);
289 input_set_abs_params(ts->input_dev, ABS_HAT0X,
290 pdata->abs_x_min, pdata->abs_x_max, 0, 0);
291 input_set_abs_params(ts->input_dev, ABS_HAT0Y,
292 pdata->abs_y_min, pdata->abs_y_max, 0, 0);
293 input_set_abs_params(ts->input_dev, ABS_PRESSURE,
294 pdata->abs_pressure_min, pdata->abs_pressure_max,
295 0, 0);
296 input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH,
297 pdata->abs_width_min, pdata->abs_width_max, 0, 0);
298
299 ret = input_register_device(ts->input_dev);
300 if (ret) {
301 dev_err(&client->dev,
302 "cy8c_ts_probe: Unable to register %s input device\n",
303 ts->input_dev->name);
304 goto err_input_register_device_failed;
305 }
306
307 if (client->irq) {
308 ret = request_irq(client->irq, cy8c_ts_irq_handler,
309 IRQF_TRIGGER_LOW, CYPRESS_TMG_NAME, ts);
310 if (ret == 0)
311 ts->use_irq = 1;
312 else
313 dev_err(&client->dev, "request_irq failed\n");
314 }
315
316 if (!ts->use_irq) {
317 hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
318 ts->timer.function = cy8c_ts_timer_func;
319 hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
320 }
321
322#ifdef CONFIG_HAS_EARLYSUSPEND
323 ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
324 ts->early_suspend.suspend = cy8c_ts_early_suspend;
325 ts->early_suspend.resume = cy8c_ts_late_resume;
326 register_early_suspend(&ts->early_suspend);
327#endif
328
329 dev_info(&client->dev, "Start touchscreen %s in %s mode\n",
330 ts->input_dev->name, (ts->use_irq ? "interrupt" : "polling"));
331
332 return 0;
333
334err_input_register_device_failed:
335 input_free_device(ts->input_dev);
336
337err_input_dev_alloc_failed:
338 if (ts->power)
339 ts->power(0);
340
341err_detect_failed:
342err_power_failed:
343 kfree(ts);
344
345err_alloc_data_failed:
346err_check_functionality_failed:
347 return ret;
348}
349
350static int cy8c_ts_remove(struct i2c_client *client)
351{
352 struct cy8c_ts_data *ts = i2c_get_clientdata(client);
353
354 unregister_early_suspend(&ts->early_suspend);
355
356 if (ts->use_irq)
357 free_irq(client->irq, ts);
358 else
359 hrtimer_cancel(&ts->timer);
360
361 input_unregister_device(ts->input_dev);
362 kfree(ts);
363
364 return 0;
365}
366
367static int cy8c_ts_suspend(struct i2c_client *client, pm_message_t mesg)
368{
369 struct cy8c_ts_data *ts = i2c_get_clientdata(client);
370 int ret;
371
372 if (ts->use_irq)
373 disable_irq_nosync(client->irq);
374 else
375 hrtimer_cancel(&ts->timer);
376
377 ret = cancel_work_sync(&ts->work);
378 if (ret && ts->use_irq)
379 enable_irq(client->irq);
380
381 if (ts->power)
382 ts->power(0);
383
384 return 0;
385}
386
387static int cy8c_ts_resume(struct i2c_client *client)
388{
389 int ret;
390 struct cy8c_ts_data *ts = i2c_get_clientdata(client);
391
392 if (ts->power) {
393 ret = ts->power(1);
394 if (ret < 0)
395 dev_err(&client->dev,
396 "cy8c_ts_resume power on failed\n");
397 msleep(10);
398
399 cy8c_init_panel(ts);
400 }
401
402 if (ts->use_irq)
403 enable_irq(client->irq);
404 else
405 hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL);
406
407 return 0;
408}
409
410#ifdef CONFIG_HAS_EARLYSUSPEND
411static void cy8c_ts_early_suspend(struct early_suspend *h)
412{
413 struct cy8c_ts_data *ts;
414 ts = container_of(h, struct cy8c_ts_data, early_suspend);
415 cy8c_ts_suspend(ts->client, PMSG_SUSPEND);
416}
417
418static void cy8c_ts_late_resume(struct early_suspend *h)
419{
420 struct cy8c_ts_data *ts;
421 ts = container_of(h, struct cy8c_ts_data, early_suspend);
422 cy8c_ts_resume(ts->client);
423}
424#endif
425
426static const struct i2c_device_id cy8c_ts_i2c_id[] = {
427 {CYPRESS_TMG_NAME, 0},
428 {}
429};
430
431static struct i2c_driver cy8c_ts_driver = {
432 .id_table = cy8c_ts_i2c_id,
433 .probe = cy8c_ts_probe,
434 .remove = cy8c_ts_remove,
435#ifndef CONFIG_HAS_EARLYSUSPEND
436 .suspend = cy8c_ts_suspend,
437 .resume = cy8c_ts_resume,
438#endif
439 .driver = {
440 .name = CYPRESS_TMG_NAME,
441 .owner = THIS_MODULE,
442 },
443};
444
445static int __devinit cy8c_ts_init(void)
446{
447 cypress_touch_wq = create_singlethread_workqueue("cypress_touch_wq");
448 if (!cypress_touch_wq)
449 return -ENOMEM;
450
451 return i2c_add_driver(&cy8c_ts_driver);
452}
453
454static void __exit cy8c_ts_exit(void)
455{
456 if (cypress_touch_wq)
457 destroy_workqueue(cypress_touch_wq);
458
459 i2c_del_driver(&cy8c_ts_driver);
460}
461
462module_init(cy8c_ts_init);
463module_exit(cy8c_ts_exit);
464
465MODULE_DESCRIPTION("Cypress TMG Touchscreen Driver");
466MODULE_LICENSE("GPL");
467