Commit | Line | Data |
---|---|---|
5c6a7a62 OS |
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 | ||
20 | struct finger { | |
21 | u8 x_low; | |
22 | u8 x_high; | |
23 | u8 y_low; | |
24 | u8 y_high; | |
25 | } __packed; | |
26 | ||
27 | struct touchdata { | |
28 | u8 status; | |
29 | struct finger finger[MAX_TOUCHES]; | |
30 | } __packed; | |
31 | ||
32 | struct panel_info { | |
33 | struct finger finger_max; | |
34 | u8 xchannel_num; | |
35 | u8 ychannel_num; | |
36 | } __packed; | |
37 | ||
38 | struct firmware_version { | |
39 | u8 id; | |
40 | u8 major; | |
41 | u8 minor; | |
42 | } __packed; | |
43 | ||
44 | struct 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 | ||
52 | static 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 = ®, | |
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 | ||
78 | static 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 | ||
106 | static 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 | ||
116 | static 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 | ||
139 | static 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 | ||
148 | static 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 | } | |
172 | static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); | |
173 | ||
174 | static struct attribute *ili210x_attributes[] = { | |
175 | &dev_attr_calibrate.attr, | |
176 | NULL, | |
177 | }; | |
178 | ||
179 | static const struct attribute_group ili210x_attr_group = { | |
180 | .attrs = ili210x_attributes, | |
181 | }; | |
182 | ||
5298cc4c | 183 | static int ili210x_i2c_probe(struct i2c_client *client, |
5c6a7a62 OS |
184 | const struct i2c_device_id *id) |
185 | { | |
186 | struct device *dev = &client->dev; | |
c838cb3d | 187 | const struct ili210x_platform_data *pdata = dev_get_platdata(dev); |
5c6a7a62 OS |
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 */ | |
b4adbbef | 255 | input_mt_init_slots(input, MAX_TOUCHES, 0); |
5c6a7a62 OS |
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 | ||
291 | err_remove_sysfs: | |
292 | sysfs_remove_group(&dev->kobj, &ili210x_attr_group); | |
293 | err_free_irq: | |
294 | free_irq(client->irq, priv); | |
295 | err_free_mem: | |
296 | input_free_device(input); | |
297 | kfree(priv); | |
298 | return error; | |
299 | } | |
300 | ||
e2619cf7 | 301 | static int ili210x_i2c_remove(struct i2c_client *client) |
5c6a7a62 OS |
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 | ||
02b6a58b | 314 | static int __maybe_unused ili210x_i2c_suspend(struct device *dev) |
5c6a7a62 OS |
315 | { |
316 | struct i2c_client *client = to_i2c_client(dev); | |
317 | ||
318 | if (device_may_wakeup(&client->dev)) | |
319 | enable_irq_wake(client->irq); | |
320 | ||
321 | return 0; | |
322 | } | |
323 | ||
02b6a58b | 324 | static int __maybe_unused ili210x_i2c_resume(struct device *dev) |
5c6a7a62 OS |
325 | { |
326 | struct i2c_client *client = to_i2c_client(dev); | |
327 | ||
328 | if (device_may_wakeup(&client->dev)) | |
329 | disable_irq_wake(client->irq); | |
330 | ||
331 | return 0; | |
332 | } | |
5c6a7a62 OS |
333 | |
334 | static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, | |
335 | ili210x_i2c_suspend, ili210x_i2c_resume); | |
336 | ||
337 | static const struct i2c_device_id ili210x_i2c_id[] = { | |
338 | { "ili210x", 0 }, | |
339 | { } | |
340 | }; | |
341 | MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); | |
342 | ||
343 | static struct i2c_driver ili210x_ts_driver = { | |
344 | .driver = { | |
345 | .name = "ili210x_i2c", | |
346 | .owner = THIS_MODULE, | |
347 | .pm = &ili210x_i2c_pm, | |
348 | }, | |
349 | .id_table = ili210x_i2c_id, | |
350 | .probe = ili210x_i2c_probe, | |
1cb0aa88 | 351 | .remove = ili210x_i2c_remove, |
5c6a7a62 OS |
352 | }; |
353 | ||
354 | module_i2c_driver(ili210x_ts_driver); | |
355 | ||
356 | MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); | |
357 | MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); | |
358 | MODULE_LICENSE("GPL"); |