aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Sobrie <olivier@sobrie.be>2012-03-17 02:57:09 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2012-03-17 15:20:44 -0400
commit5c6a7a62c130afef3d61c1dee153012231ff5cd9 (patch)
tree19052f6ddcb413ebc967f9f32d923b9fddb24a04
parent14b5842ebf552d242b0238f70d0730a564065fb0 (diff)
Input: ili210x - add support for Ilitek ILI210x based touchscreens
The driver supports chipsets ILI2102, ILI2102s, ILI2103, ILI2103s and ILI2105. Such kind of controllers can be found in Amazon Kindle Fire devices. Reviewed-by: Jan Paesmans <jan.paesmans@gmail.com> Reviewed-by: Henrik Rydberg <rydberg@euromail.se> Signed-off-by: Olivier Sobrie <olivier@sobrie.be> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r--drivers/input/touchscreen/Kconfig15
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ili210x.c360
-rw-r--r--include/linux/input/ili210x.h10
4 files changed, 386 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index fc087b3c95c..97b31a0e052 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -243,6 +243,21 @@ config TOUCHSCREEN_FUJITSU
243 To compile this driver as a module, choose M here: the 243 To compile this driver as a module, choose M here: the
244 module will be called fujitsu-ts. 244 module will be called fujitsu-ts.
245 245
246config TOUCHSCREEN_ILI210X
247 tristate "Ilitek ILI210X based touchscreen"
248 depends on I2C
249 help
250 Say Y here if you have a ILI210X based touchscreen
251 controller. This driver supports models ILI2102,
252 ILI2102s, ILI2103, ILI2103s and ILI2105.
253 Such kind of chipsets can be found in Amazon Kindle Fire
254 touchscreens.
255
256 If unsure, say N.
257
258 To compile this driver as a module, choose M here: the
259 module will be called ili210x.
260
246config TOUCHSCREEN_S3C2410 261config TOUCHSCREEN_S3C2410
247 tristate "Samsung S3C2410/generic touchscreen input driver" 262 tristate "Samsung S3C2410/generic touchscreen input driver"
248 depends on ARCH_S3C2410 || SAMSUNG_DEV_TS 263 depends on ARCH_S3C2410 || SAMSUNG_DEV_TS
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index e748fb83775..3d5cf8cbf89 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
29obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o 29obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
30obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o 30obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
31obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o 31obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
32obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
32obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o 33obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
33obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o 34obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
34obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o 35obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
new file mode 100644
index 00000000000..c0044175a92
--- /dev/null
+++ b/drivers/input/touchscreen/ili210x.c
@@ -0,0 +1,360 @@
1#include <linux/module.h>
2#include <linux/i2c.h>
3#include <linux/interrupt.h>
4#include <linux/slab.h>
5#include <linux/input.h>
6#include <linux/input/mt.h>
7#include <linux/delay.h>
8#include <linux/workqueue.h>
9#include <linux/input/ili210x.h>
10
11#define MAX_TOUCHES 2
12#define DEFAULT_POLL_PERIOD 20
13
14/* Touchscreen commands */
15#define REG_TOUCHDATA 0x10
16#define REG_PANEL_INFO 0x20
17#define REG_FIRMWARE_VERSION 0x40
18#define REG_CALIBRATE 0xcc
19
20struct finger {
21 u8 x_low;
22 u8 x_high;
23 u8 y_low;
24 u8 y_high;
25} __packed;
26
27struct touchdata {
28 u8 status;
29 struct finger finger[MAX_TOUCHES];
30} __packed;
31
32struct panel_info {
33 struct finger finger_max;
34 u8 xchannel_num;
35 u8 ychannel_num;
36} __packed;
37
38struct firmware_version {
39 u8 id;
40 u8 major;
41 u8 minor;
42} __packed;
43
44struct ili210x {
45 struct i2c_client *client;
46 struct input_dev *input;
47 bool (*get_pendown_state)(void);
48 unsigned int poll_period;
49 struct delayed_work dwork;
50};
51
52static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
53 size_t len)
54{
55 struct i2c_msg msg[2] = {
56 {
57 .addr = client->addr,
58 .flags = 0,
59 .len = 1,
60 .buf = &reg,
61 },
62 {
63 .addr = client->addr,
64 .flags = I2C_M_RD,
65 .len = len,
66 .buf = buf,
67 }
68 };
69
70 if (i2c_transfer(client->adapter, msg, 2) != 2) {
71 dev_err(&client->dev, "i2c transfer failed\n");
72 return -EIO;
73 }
74
75 return 0;
76}
77
78static void ili210x_report_events(struct input_dev *input,
79 const struct touchdata *touchdata)
80{
81 int i;
82 bool touch;
83 unsigned int x, y;
84 const struct finger *finger;
85
86 for (i = 0; i < MAX_TOUCHES; i++) {
87 input_mt_slot(input, i);
88
89 finger = &touchdata->finger[i];
90
91 touch = touchdata->status & (1 << i);
92 input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
93 if (touch) {
94 x = finger->x_low | (finger->x_high << 8);
95 y = finger->y_low | (finger->y_high << 8);
96
97 input_report_abs(input, ABS_MT_POSITION_X, x);
98 input_report_abs(input, ABS_MT_POSITION_Y, y);
99 }
100 }
101
102 input_mt_report_pointer_emulation(input, false);
103 input_sync(input);
104}
105
106static bool get_pendown_state(const struct ili210x *priv)
107{
108 bool state = false;
109
110 if (priv->get_pendown_state)
111 state = priv->get_pendown_state();
112
113 return state;
114}
115
116static void ili210x_work(struct work_struct *work)
117{
118 struct ili210x *priv = container_of(work, struct ili210x,
119 dwork.work);
120 struct i2c_client *client = priv->client;
121 struct touchdata touchdata;
122 int error;
123
124 error = ili210x_read_reg(client, REG_TOUCHDATA,
125 &touchdata, sizeof(touchdata));
126 if (error) {
127 dev_err(&client->dev,
128 "Unable to get touchdata, err = %d\n", error);
129 return;
130 }
131
132 ili210x_report_events(priv->input, &touchdata);
133
134 if ((touchdata.status & 0xf3) || get_pendown_state(priv))
135 schedule_delayed_work(&priv->dwork,
136 msecs_to_jiffies(priv->poll_period));
137}
138
139static irqreturn_t ili210x_irq(int irq, void *irq_data)
140{
141 struct ili210x *priv = irq_data;
142
143 schedule_delayed_work(&priv->dwork, 0);
144
145 return IRQ_HANDLED;
146}
147
148static ssize_t ili210x_calibrate(struct device *dev,
149 struct device_attribute *attr,
150 const char *buf, size_t count)
151{
152 struct i2c_client *client = to_i2c_client(dev);
153 struct ili210x *priv = i2c_get_clientdata(client);
154 unsigned long calibrate;
155 int rc;
156 u8 cmd = REG_CALIBRATE;
157
158 if (kstrtoul(buf, 10, &calibrate))
159 return -EINVAL;
160
161 if (calibrate > 1)
162 return -EINVAL;
163
164 if (calibrate) {
165 rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
166 if (rc != sizeof(cmd))
167 return -EIO;
168 }
169
170 return count;
171}
172static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate);
173
174static struct attribute *ili210x_attributes[] = {
175 &dev_attr_calibrate.attr,
176 NULL,
177};
178
179static const struct attribute_group ili210x_attr_group = {
180 .attrs = ili210x_attributes,
181};
182
183static int __devinit ili210x_i2c_probe(struct i2c_client *client,
184 const struct i2c_device_id *id)
185{
186 struct device *dev = &client->dev;
187 const struct ili210x_platform_data *pdata = dev->platform_data;
188 struct ili210x *priv;
189 struct input_dev *input;
190 struct panel_info panel;
191 struct firmware_version firmware;
192 int xmax, ymax;
193 int error;
194
195 dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
196
197 if (!pdata) {
198 dev_err(dev, "No platform data!\n");
199 return -EINVAL;
200 }
201
202 if (client->irq <= 0) {
203 dev_err(dev, "No IRQ!\n");
204 return -EINVAL;
205 }
206
207 /* Get firmware version */
208 error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
209 &firmware, sizeof(firmware));
210 if (error) {
211 dev_err(dev, "Failed to get firmware version, err: %d\n",
212 error);
213 return error;
214 }
215
216 /* get panel info */
217 error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
218 if (error) {
219 dev_err(dev, "Failed to get panel informations, err: %d\n",
220 error);
221 return error;
222 }
223
224 xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
225 ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
226
227 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
228 input = input_allocate_device();
229 if (!priv || !input) {
230 error = -ENOMEM;
231 goto err_free_mem;
232 }
233
234 priv->client = client;
235 priv->input = input;
236 priv->get_pendown_state = pdata->get_pendown_state;
237 priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
238 INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
239
240 /* Setup input device */
241 input->name = "ILI210x Touchscreen";
242 input->id.bustype = BUS_I2C;
243 input->dev.parent = dev;
244
245 __set_bit(EV_SYN, input->evbit);
246 __set_bit(EV_KEY, input->evbit);
247 __set_bit(EV_ABS, input->evbit);
248 __set_bit(BTN_TOUCH, input->keybit);
249
250 /* Single touch */
251 input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
252 input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
253
254 /* Multi touch */
255 input_mt_init_slots(input, MAX_TOUCHES);
256 input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
257 input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
258
259 input_set_drvdata(input, priv);
260 i2c_set_clientdata(client, priv);
261
262 error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
263 client->name, priv);
264 if (error) {
265 dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
266 error);
267 goto err_free_mem;
268 }
269
270 error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
271 if (error) {
272 dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
273 error);
274 goto err_free_irq;
275 }
276
277 error = input_register_device(priv->input);
278 if (error) {
279 dev_err(dev, "Cannot regiser input device, err: %d\n", error);
280 goto err_remove_sysfs;
281 }
282
283 device_init_wakeup(&client->dev, 1);
284
285 dev_dbg(dev,
286 "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
287 client->irq, firmware.id, firmware.major, firmware.minor);
288
289 return 0;
290
291err_remove_sysfs:
292 sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
293err_free_irq:
294 free_irq(client->irq, priv);
295err_free_mem:
296 input_free_device(input);
297 kfree(priv);
298 return error;
299}
300
301static int __devexit ili210x_i2c_remove(struct i2c_client *client)
302{
303 struct ili210x *priv = i2c_get_clientdata(client);
304
305 sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
306 free_irq(priv->client->irq, priv);
307 cancel_delayed_work_sync(&priv->dwork);
308 input_unregister_device(priv->input);
309 kfree(priv);
310
311 return 0;
312}
313
314#ifdef CONFIG_PM_SLEEP
315static int ili210x_i2c_suspend(struct device *dev)
316{
317 struct i2c_client *client = to_i2c_client(dev);
318
319 if (device_may_wakeup(&client->dev))
320 enable_irq_wake(client->irq);
321
322 return 0;
323}
324
325static int ili210x_i2c_resume(struct device *dev)
326{
327 struct i2c_client *client = to_i2c_client(dev);
328
329 if (device_may_wakeup(&client->dev))
330 disable_irq_wake(client->irq);
331
332 return 0;
333}
334#endif
335
336static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
337 ili210x_i2c_suspend, ili210x_i2c_resume);
338
339static const struct i2c_device_id ili210x_i2c_id[] = {
340 { "ili210x", 0 },
341 { }
342};
343MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
344
345static struct i2c_driver ili210x_ts_driver = {
346 .driver = {
347 .name = "ili210x_i2c",
348 .owner = THIS_MODULE,
349 .pm = &ili210x_i2c_pm,
350 },
351 .id_table = ili210x_i2c_id,
352 .probe = ili210x_i2c_probe,
353 .remove = __devexit_p(ili210x_i2c_remove),
354};
355
356module_i2c_driver(ili210x_ts_driver);
357
358MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
359MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
360MODULE_LICENSE("GPL");
diff --git a/include/linux/input/ili210x.h b/include/linux/input/ili210x.h
new file mode 100644
index 00000000000..a5471245a13
--- /dev/null
+++ b/include/linux/input/ili210x.h
@@ -0,0 +1,10 @@
1#ifndef _ILI210X_H
2#define _ILI210X_H
3
4struct ili210x_platform_data {
5 unsigned long irq_flags;
6 unsigned int poll_period;
7 bool (*get_pendown_state)(void);
8};
9
10#endif