1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for MStar msg2638 touchscreens |
4 | * |
5 | * Copyright (c) 2021 Vincent Knecht <vincent.knecht@mailoo.org> |
6 | * |
7 | * Checksum and IRQ handler based on mstar_drv_common.c and |
8 | * mstar_drv_mutual_fw_control.c |
9 | * Copyright (c) 2006-2012 MStar Semiconductor, Inc. |
10 | * |
11 | * Driver structure based on zinitix.c by Michael Srba <Michael.Srba@seznam.cz> |
12 | */ |
13 | |
14 | #include <linux/delay.h> |
15 | #include <linux/gpio/consumer.h> |
16 | #include <linux/i2c.h> |
17 | #include <linux/input.h> |
18 | #include <linux/input/mt.h> |
19 | #include <linux/input/touchscreen.h> |
20 | #include <linux/interrupt.h> |
21 | #include <linux/kernel.h> |
22 | #include <linux/mod_devicetable.h> |
23 | #include <linux/module.h> |
24 | #include <linux/property.h> |
25 | #include <linux/regulator/consumer.h> |
26 | #include <linux/slab.h> |
27 | |
28 | #define MODE_DATA_RAW 0x5A |
29 | |
30 | #define MSG2138_MAX_FINGERS 2 |
31 | #define MSG2638_MAX_FINGERS 5 |
32 | |
33 | #define MAX_BUTTONS 4 |
34 | |
35 | #define CHIP_ON_DELAY_MS 15 |
36 | #define FIRMWARE_ON_DELAY_MS 50 |
37 | #define RESET_DELAY_MIN_US 10000 |
38 | #define RESET_DELAY_MAX_US 11000 |
39 | |
40 | struct msg_chip_data { |
41 | irq_handler_t irq_handler; |
42 | unsigned int max_fingers; |
43 | }; |
44 | |
45 | struct msg2138_packet { |
46 | u8 xy_hi; /* higher bits of x and y coordinates */ |
47 | u8 x_low; |
48 | u8 y_low; |
49 | }; |
50 | |
51 | struct msg2138_touch_event { |
52 | u8 magic; |
53 | struct msg2138_packet pkt[MSG2138_MAX_FINGERS]; |
54 | u8 checksum; |
55 | }; |
56 | |
57 | struct msg2638_packet { |
58 | u8 xy_hi; /* higher bits of x and y coordinates */ |
59 | u8 x_low; |
60 | u8 y_low; |
61 | u8 pressure; |
62 | }; |
63 | |
64 | struct msg2638_touch_event { |
65 | u8 mode; |
66 | struct msg2638_packet pkt[MSG2638_MAX_FINGERS]; |
67 | u8 proximity; |
68 | u8 checksum; |
69 | }; |
70 | |
71 | struct msg2638_ts_data { |
72 | struct i2c_client *client; |
73 | struct input_dev *input_dev; |
74 | struct touchscreen_properties prop; |
75 | struct regulator_bulk_data supplies[2]; |
76 | struct gpio_desc *reset_gpiod; |
77 | int max_fingers; |
78 | u32 keycodes[MAX_BUTTONS]; |
79 | int num_keycodes; |
80 | }; |
81 | |
82 | static u8 msg2638_checksum(u8 *data, u32 length) |
83 | { |
84 | s32 sum = 0; |
85 | u32 i; |
86 | |
87 | for (i = 0; i < length; i++) |
88 | sum += data[i]; |
89 | |
90 | return (u8)((-sum) & 0xFF); |
91 | } |
92 | |
93 | static void msg2138_report_keys(struct msg2638_ts_data *msg2638, u8 keys) |
94 | { |
95 | int i; |
96 | |
97 | /* keys can be 0x00 or 0xff when all keys have been released */ |
98 | if (keys == 0xff) |
99 | keys = 0; |
100 | |
101 | for (i = 0; i < msg2638->num_keycodes; ++i) |
102 | input_report_key(dev: msg2638->input_dev, code: msg2638->keycodes[i], |
103 | value: keys & BIT(i)); |
104 | } |
105 | |
106 | static irqreturn_t msg2138_ts_irq_handler(int irq, void *msg2638_handler) |
107 | { |
108 | struct msg2638_ts_data *msg2638 = msg2638_handler; |
109 | struct i2c_client *client = msg2638->client; |
110 | struct input_dev *input = msg2638->input_dev; |
111 | struct msg2138_touch_event touch_event; |
112 | u32 len = sizeof(touch_event); |
113 | struct i2c_msg msg[] = { |
114 | { |
115 | .addr = client->addr, |
116 | .flags = I2C_M_RD, |
117 | .len = sizeof(touch_event), |
118 | .buf = (u8 *)&touch_event, |
119 | }, |
120 | }; |
121 | struct msg2138_packet *p0, *p1; |
122 | u16 x, y, delta_x, delta_y; |
123 | int ret; |
124 | |
125 | ret = i2c_transfer(adap: client->adapter, msgs: msg, ARRAY_SIZE(msg)); |
126 | if (ret != ARRAY_SIZE(msg)) { |
127 | dev_err(&client->dev, |
128 | "Failed I2C transfer in irq handler: %d\n" , |
129 | ret < 0 ? ret : -EIO); |
130 | goto out; |
131 | } |
132 | |
133 | if (msg2638_checksum(data: (u8 *)&touch_event, length: len - 1) != |
134 | touch_event.checksum) { |
135 | dev_err(&client->dev, "Failed checksum!\n" ); |
136 | goto out; |
137 | } |
138 | |
139 | p0 = &touch_event.pkt[0]; |
140 | p1 = &touch_event.pkt[1]; |
141 | |
142 | /* Ignore non-pressed finger data, but check for key code */ |
143 | if (p0->xy_hi == 0xFF && p0->x_low == 0xFF && p0->y_low == 0xFF) { |
144 | if (p1->xy_hi == 0xFF && p1->y_low == 0xFF) |
145 | msg2138_report_keys(msg2638, keys: p1->x_low); |
146 | goto report; |
147 | } |
148 | |
149 | x = ((p0->xy_hi & 0xF0) << 4) | p0->x_low; |
150 | y = ((p0->xy_hi & 0x0F) << 8) | p0->y_low; |
151 | |
152 | input_mt_slot(dev: input, slot: 0); |
153 | input_mt_report_slot_state(dev: input, MT_TOOL_FINGER, active: true); |
154 | touchscreen_report_pos(input, prop: &msg2638->prop, x, y, multitouch: true); |
155 | |
156 | /* Ignore non-pressed finger data */ |
157 | if (p1->xy_hi == 0xFF && p1->x_low == 0xFF && p1->y_low == 0xFF) |
158 | goto report; |
159 | |
160 | /* Second finger is reported as a delta position */ |
161 | delta_x = ((p1->xy_hi & 0xF0) << 4) | p1->x_low; |
162 | delta_y = ((p1->xy_hi & 0x0F) << 8) | p1->y_low; |
163 | |
164 | /* Ignore second finger if both deltas equal 0 */ |
165 | if (delta_x == 0 && delta_y == 0) |
166 | goto report; |
167 | |
168 | x += delta_x; |
169 | y += delta_y; |
170 | |
171 | input_mt_slot(dev: input, slot: 1); |
172 | input_mt_report_slot_state(dev: input, MT_TOOL_FINGER, active: true); |
173 | touchscreen_report_pos(input, prop: &msg2638->prop, x, y, multitouch: true); |
174 | |
175 | report: |
176 | input_mt_sync_frame(dev: msg2638->input_dev); |
177 | input_sync(dev: msg2638->input_dev); |
178 | |
179 | out: |
180 | return IRQ_HANDLED; |
181 | } |
182 | |
183 | static irqreturn_t msg2638_ts_irq_handler(int irq, void *msg2638_handler) |
184 | { |
185 | struct msg2638_ts_data *msg2638 = msg2638_handler; |
186 | struct i2c_client *client = msg2638->client; |
187 | struct input_dev *input = msg2638->input_dev; |
188 | struct msg2638_touch_event touch_event; |
189 | u32 len = sizeof(touch_event); |
190 | struct i2c_msg msg[] = { |
191 | { |
192 | .addr = client->addr, |
193 | .flags = I2C_M_RD, |
194 | .len = sizeof(touch_event), |
195 | .buf = (u8 *)&touch_event, |
196 | }, |
197 | }; |
198 | struct msg2638_packet *p; |
199 | u16 x, y; |
200 | int ret; |
201 | int i; |
202 | |
203 | ret = i2c_transfer(adap: client->adapter, msgs: msg, ARRAY_SIZE(msg)); |
204 | if (ret != ARRAY_SIZE(msg)) { |
205 | dev_err(&client->dev, |
206 | "Failed I2C transfer in irq handler: %d\n" , |
207 | ret < 0 ? ret : -EIO); |
208 | goto out; |
209 | } |
210 | |
211 | if (touch_event.mode != MODE_DATA_RAW) |
212 | goto out; |
213 | |
214 | if (msg2638_checksum(data: (u8 *)&touch_event, length: len - 1) != |
215 | touch_event.checksum) { |
216 | dev_err(&client->dev, "Failed checksum!\n" ); |
217 | goto out; |
218 | } |
219 | |
220 | for (i = 0; i < msg2638->max_fingers; i++) { |
221 | p = &touch_event.pkt[i]; |
222 | |
223 | /* Ignore non-pressed finger data */ |
224 | if (p->xy_hi == 0xFF && p->x_low == 0xFF && p->y_low == 0xFF) |
225 | continue; |
226 | |
227 | x = (((p->xy_hi & 0xF0) << 4) | p->x_low); |
228 | y = (((p->xy_hi & 0x0F) << 8) | p->y_low); |
229 | |
230 | input_mt_slot(dev: input, slot: i); |
231 | input_mt_report_slot_state(dev: input, MT_TOOL_FINGER, active: true); |
232 | touchscreen_report_pos(input, prop: &msg2638->prop, x, y, multitouch: true); |
233 | } |
234 | |
235 | input_mt_sync_frame(dev: msg2638->input_dev); |
236 | input_sync(dev: msg2638->input_dev); |
237 | |
238 | out: |
239 | return IRQ_HANDLED; |
240 | } |
241 | |
242 | static void msg2638_reset(struct msg2638_ts_data *msg2638) |
243 | { |
244 | gpiod_set_value_cansleep(desc: msg2638->reset_gpiod, value: 1); |
245 | usleep_range(RESET_DELAY_MIN_US, RESET_DELAY_MAX_US); |
246 | gpiod_set_value_cansleep(desc: msg2638->reset_gpiod, value: 0); |
247 | msleep(FIRMWARE_ON_DELAY_MS); |
248 | } |
249 | |
250 | static int msg2638_start(struct msg2638_ts_data *msg2638) |
251 | { |
252 | int error; |
253 | |
254 | error = regulator_bulk_enable(ARRAY_SIZE(msg2638->supplies), |
255 | consumers: msg2638->supplies); |
256 | if (error) { |
257 | dev_err(&msg2638->client->dev, |
258 | "Failed to enable regulators: %d\n" , error); |
259 | return error; |
260 | } |
261 | |
262 | msleep(CHIP_ON_DELAY_MS); |
263 | |
264 | msg2638_reset(msg2638); |
265 | |
266 | enable_irq(irq: msg2638->client->irq); |
267 | |
268 | return 0; |
269 | } |
270 | |
271 | static int msg2638_stop(struct msg2638_ts_data *msg2638) |
272 | { |
273 | int error; |
274 | |
275 | disable_irq(irq: msg2638->client->irq); |
276 | |
277 | error = regulator_bulk_disable(ARRAY_SIZE(msg2638->supplies), |
278 | consumers: msg2638->supplies); |
279 | if (error) { |
280 | dev_err(&msg2638->client->dev, |
281 | "Failed to disable regulators: %d\n" , error); |
282 | return error; |
283 | } |
284 | |
285 | return 0; |
286 | } |
287 | |
288 | static int msg2638_input_open(struct input_dev *dev) |
289 | { |
290 | struct msg2638_ts_data *msg2638 = input_get_drvdata(dev); |
291 | |
292 | return msg2638_start(msg2638); |
293 | } |
294 | |
295 | static void msg2638_input_close(struct input_dev *dev) |
296 | { |
297 | struct msg2638_ts_data *msg2638 = input_get_drvdata(dev); |
298 | |
299 | msg2638_stop(msg2638); |
300 | } |
301 | |
302 | static int msg2638_init_input_dev(struct msg2638_ts_data *msg2638) |
303 | { |
304 | struct device *dev = &msg2638->client->dev; |
305 | struct input_dev *input_dev; |
306 | int error; |
307 | int i; |
308 | |
309 | input_dev = devm_input_allocate_device(dev); |
310 | if (!input_dev) { |
311 | dev_err(dev, "Failed to allocate input device.\n" ); |
312 | return -ENOMEM; |
313 | } |
314 | |
315 | input_set_drvdata(dev: input_dev, data: msg2638); |
316 | msg2638->input_dev = input_dev; |
317 | |
318 | input_dev->name = "MStar TouchScreen" ; |
319 | input_dev->phys = "input/ts" ; |
320 | input_dev->id.bustype = BUS_I2C; |
321 | input_dev->open = msg2638_input_open; |
322 | input_dev->close = msg2638_input_close; |
323 | |
324 | if (msg2638->num_keycodes) { |
325 | input_dev->keycode = msg2638->keycodes; |
326 | input_dev->keycodemax = msg2638->num_keycodes; |
327 | input_dev->keycodesize = sizeof(msg2638->keycodes[0]); |
328 | for (i = 0; i < msg2638->num_keycodes; i++) |
329 | input_set_capability(dev: input_dev, |
330 | EV_KEY, code: msg2638->keycodes[i]); |
331 | } |
332 | |
333 | input_set_capability(dev: input_dev, EV_ABS, ABS_MT_POSITION_X); |
334 | input_set_capability(dev: input_dev, EV_ABS, ABS_MT_POSITION_Y); |
335 | |
336 | touchscreen_parse_properties(input: input_dev, multitouch: true, prop: &msg2638->prop); |
337 | if (!msg2638->prop.max_x || !msg2638->prop.max_y) { |
338 | dev_err(dev, "touchscreen-size-x and/or touchscreen-size-y not set in properties\n" ); |
339 | return -EINVAL; |
340 | } |
341 | |
342 | error = input_mt_init_slots(dev: input_dev, num_slots: msg2638->max_fingers, |
343 | INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); |
344 | if (error) { |
345 | dev_err(dev, "Failed to initialize MT slots: %d\n" , error); |
346 | return error; |
347 | } |
348 | |
349 | error = input_register_device(input_dev); |
350 | if (error) { |
351 | dev_err(dev, "Failed to register input device: %d\n" , error); |
352 | return error; |
353 | } |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | static int msg2638_ts_probe(struct i2c_client *client) |
359 | { |
360 | const struct msg_chip_data *chip_data; |
361 | struct device *dev = &client->dev; |
362 | struct msg2638_ts_data *msg2638; |
363 | int error; |
364 | |
365 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) { |
366 | dev_err(dev, "Failed to assert adapter's support for plain I2C.\n" ); |
367 | return -ENXIO; |
368 | } |
369 | |
370 | msg2638 = devm_kzalloc(dev, size: sizeof(*msg2638), GFP_KERNEL); |
371 | if (!msg2638) |
372 | return -ENOMEM; |
373 | |
374 | msg2638->client = client; |
375 | i2c_set_clientdata(client, data: msg2638); |
376 | |
377 | chip_data = device_get_match_data(dev: &client->dev); |
378 | if (!chip_data || !chip_data->max_fingers) { |
379 | dev_err(dev, "Invalid or missing chip data\n" ); |
380 | return -EINVAL; |
381 | } |
382 | |
383 | msg2638->max_fingers = chip_data->max_fingers; |
384 | |
385 | msg2638->supplies[0].supply = "vdd" ; |
386 | msg2638->supplies[1].supply = "vddio" ; |
387 | error = devm_regulator_bulk_get(dev, ARRAY_SIZE(msg2638->supplies), |
388 | consumers: msg2638->supplies); |
389 | if (error) { |
390 | dev_err(dev, "Failed to get regulators: %d\n" , error); |
391 | return error; |
392 | } |
393 | |
394 | msg2638->reset_gpiod = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
395 | if (IS_ERR(ptr: msg2638->reset_gpiod)) { |
396 | error = PTR_ERR(ptr: msg2638->reset_gpiod); |
397 | dev_err(dev, "Failed to request reset GPIO: %d\n" , error); |
398 | return error; |
399 | } |
400 | |
401 | msg2638->num_keycodes = device_property_count_u32(dev, |
402 | propname: "linux,keycodes" ); |
403 | if (msg2638->num_keycodes == -EINVAL) { |
404 | msg2638->num_keycodes = 0; |
405 | } else if (msg2638->num_keycodes < 0) { |
406 | dev_err(dev, "Unable to parse linux,keycodes property: %d\n" , |
407 | msg2638->num_keycodes); |
408 | return msg2638->num_keycodes; |
409 | } else if (msg2638->num_keycodes > ARRAY_SIZE(msg2638->keycodes)) { |
410 | dev_warn(dev, "Found %d linux,keycodes but max is %zd, ignoring the rest\n" , |
411 | msg2638->num_keycodes, ARRAY_SIZE(msg2638->keycodes)); |
412 | msg2638->num_keycodes = ARRAY_SIZE(msg2638->keycodes); |
413 | } |
414 | |
415 | if (msg2638->num_keycodes > 0) { |
416 | error = device_property_read_u32_array(dev, propname: "linux,keycodes" , |
417 | val: msg2638->keycodes, |
418 | nval: msg2638->num_keycodes); |
419 | if (error) { |
420 | dev_err(dev, "Unable to read linux,keycodes values: %d\n" , |
421 | error); |
422 | return error; |
423 | } |
424 | } |
425 | |
426 | error = devm_request_threaded_irq(dev, irq: client->irq, |
427 | NULL, thread_fn: chip_data->irq_handler, |
428 | IRQF_ONESHOT | IRQF_NO_AUTOEN, |
429 | devname: client->name, dev_id: msg2638); |
430 | if (error) { |
431 | dev_err(dev, "Failed to request IRQ: %d\n" , error); |
432 | return error; |
433 | } |
434 | |
435 | error = msg2638_init_input_dev(msg2638); |
436 | if (error) { |
437 | dev_err(dev, "Failed to initialize input device: %d\n" , error); |
438 | return error; |
439 | } |
440 | |
441 | return 0; |
442 | } |
443 | |
444 | static int msg2638_suspend(struct device *dev) |
445 | { |
446 | struct i2c_client *client = to_i2c_client(dev); |
447 | struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client); |
448 | |
449 | mutex_lock(&msg2638->input_dev->mutex); |
450 | |
451 | if (input_device_enabled(dev: msg2638->input_dev)) |
452 | msg2638_stop(msg2638); |
453 | |
454 | mutex_unlock(lock: &msg2638->input_dev->mutex); |
455 | |
456 | return 0; |
457 | } |
458 | |
459 | static int msg2638_resume(struct device *dev) |
460 | { |
461 | struct i2c_client *client = to_i2c_client(dev); |
462 | struct msg2638_ts_data *msg2638 = i2c_get_clientdata(client); |
463 | int ret = 0; |
464 | |
465 | mutex_lock(&msg2638->input_dev->mutex); |
466 | |
467 | if (input_device_enabled(dev: msg2638->input_dev)) |
468 | ret = msg2638_start(msg2638); |
469 | |
470 | mutex_unlock(lock: &msg2638->input_dev->mutex); |
471 | |
472 | return ret; |
473 | } |
474 | |
475 | static DEFINE_SIMPLE_DEV_PM_OPS(msg2638_pm_ops, msg2638_suspend, msg2638_resume); |
476 | |
477 | static const struct msg_chip_data msg2138_data = { |
478 | .irq_handler = msg2138_ts_irq_handler, |
479 | .max_fingers = MSG2138_MAX_FINGERS, |
480 | }; |
481 | |
482 | static const struct msg_chip_data msg2638_data = { |
483 | .irq_handler = msg2638_ts_irq_handler, |
484 | .max_fingers = MSG2638_MAX_FINGERS, |
485 | }; |
486 | |
487 | static const struct of_device_id msg2638_of_match[] = { |
488 | { .compatible = "mstar,msg2138" , .data = &msg2138_data }, |
489 | { .compatible = "mstar,msg2638" , .data = &msg2638_data }, |
490 | { } |
491 | }; |
492 | MODULE_DEVICE_TABLE(of, msg2638_of_match); |
493 | |
494 | static struct i2c_driver msg2638_ts_driver = { |
495 | .probe = msg2638_ts_probe, |
496 | .driver = { |
497 | .name = "MStar-TS" , |
498 | .pm = pm_sleep_ptr(&msg2638_pm_ops), |
499 | .of_match_table = msg2638_of_match, |
500 | }, |
501 | }; |
502 | module_i2c_driver(msg2638_ts_driver); |
503 | |
504 | MODULE_AUTHOR("Vincent Knecht <vincent.knecht@mailoo.org>" ); |
505 | MODULE_DESCRIPTION("MStar MSG2638 touchscreen driver" ); |
506 | MODULE_LICENSE("GPL v2" ); |
507 | |