1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Parade TrueTouch(TM) Standard Product V5 Module. |
4 | * |
5 | * Copyright (C) 2015 Parade Technologies |
6 | * Copyright (C) 2012-2015 Cypress Semiconductor |
7 | * Copyright (C) 2018 Bootlin |
8 | * |
9 | * Authors: Mylène Josserand <mylene.josserand@bootlin.com> |
10 | * Alistair Francis <alistair@alistair23.me> |
11 | */ |
12 | |
13 | #include <linux/crc-itu-t.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/device.h> |
16 | #include <linux/gpio/consumer.h> |
17 | #include <linux/input/mt.h> |
18 | #include <linux/input/touchscreen.h> |
19 | #include <linux/interrupt.h> |
20 | #include <linux/i2c.h> |
21 | #include <linux/mod_devicetable.h> |
22 | #include <linux/module.h> |
23 | #include <linux/regmap.h> |
24 | #include <asm/unaligned.h> |
25 | |
26 | #define CYTTSP5_NAME "cyttsp5" |
27 | #define CY_I2C_DATA_SIZE (2 * 256) |
28 | #define HID_VERSION 0x0100 |
29 | #define CY_MAX_INPUT 512 |
30 | #define CYTTSP5_PREALLOCATED_CMD_BUFFER 32 |
31 | #define CY_BITS_PER_BTN 1 |
32 | #define CY_NUM_BTN_EVENT_ID GENMASK(CY_BITS_PER_BTN - 1, 0) |
33 | |
34 | #define MAX_AREA 255 |
35 | #define HID_OUTPUT_BL_SOP 0x1 |
36 | #define HID_OUTPUT_BL_EOP 0x17 |
37 | #define HID_OUTPUT_BL_LAUNCH_APP 0x3B |
38 | #define HID_OUTPUT_BL_LAUNCH_APP_SIZE 11 |
39 | #define HID_OUTPUT_GET_SYSINFO 0x2 |
40 | #define HID_OUTPUT_GET_SYSINFO_SIZE 5 |
41 | #define HID_OUTPUT_MAX_CMD_SIZE 12 |
42 | |
43 | #define HID_DESC_REG 0x1 |
44 | #define HID_INPUT_REG 0x3 |
45 | #define HID_OUTPUT_REG 0x4 |
46 | #define HID_COMMAND_REG 0x5 |
47 | |
48 | #define REPORT_ID_TOUCH 0x1 |
49 | #define REPORT_ID_BTN 0x3 |
50 | #define REPORT_SIZE_5 5 |
51 | #define REPORT_SIZE_8 8 |
52 | #define REPORT_SIZE_16 16 |
53 | |
54 | /* Touch reports offsets */ |
55 | /* Header offsets */ |
56 | #define TOUCH_REPORT_DESC_HDR_CONTACTCOUNT 16 |
57 | /* Record offsets */ |
58 | #define TOUCH_REPORT_DESC_CONTACTID 8 |
59 | #define TOUCH_REPORT_DESC_X 16 |
60 | #define TOUCH_REPORT_DESC_Y 32 |
61 | #define TOUCH_REPORT_DESC_P 48 |
62 | #define TOUCH_REPORT_DESC_MAJ 56 |
63 | #define TOUCH_REPORT_DESC_MIN 64 |
64 | |
65 | /* HID */ |
66 | #define HID_TOUCH_REPORT_ID 0x1 |
67 | #define HID_BTN_REPORT_ID 0x3 |
68 | #define HID_APP_RESPONSE_REPORT_ID 0x1F |
69 | #define HID_APP_OUTPUT_REPORT_ID 0x2F |
70 | #define HID_BL_RESPONSE_REPORT_ID 0x30 |
71 | #define HID_BL_OUTPUT_REPORT_ID 0x40 |
72 | #define HID_RESPONSE_REPORT_ID 0xF0 |
73 | |
74 | #define HID_OUTPUT_RESPONSE_REPORT_OFFSET 2 |
75 | #define HID_OUTPUT_RESPONSE_CMD_OFFSET 4 |
76 | #define HID_OUTPUT_RESPONSE_CMD_MASK GENMASK(6, 0) |
77 | |
78 | #define HID_SYSINFO_SENSING_OFFSET 33 |
79 | #define HID_SYSINFO_BTN_OFFSET 48 |
80 | #define HID_SYSINFO_BTN_MASK GENMASK(7, 0) |
81 | #define HID_SYSINFO_MAX_BTN 8 |
82 | |
83 | #define HID_CMD_SET_POWER 0x8 |
84 | |
85 | #define HID_POWER_ON 0x0 |
86 | #define HID_POWER_SLEEP 0x1 |
87 | |
88 | #define CY_HID_OUTPUT_TIMEOUT_MS 200 |
89 | #define CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT_MS 3000 |
90 | #define CY_HID_GET_HID_DESCRIPTOR_TIMEOUT_MS 4000 |
91 | #define CY_HID_SET_POWER_TIMEOUT 500 |
92 | |
93 | /* maximum number of concurrent tracks */ |
94 | #define TOUCH_REPORT_SIZE 10 |
95 | #define 7 |
96 | #define BTN_REPORT_SIZE 9 |
97 | #define 5 |
98 | |
99 | #define MAX_CY_TCH_T_IDS 32 |
100 | |
101 | /* All usage pages for Touch Report */ |
102 | #define TOUCH_REPORT_USAGE_PG_X 0x00010030 |
103 | #define TOUCH_REPORT_USAGE_PG_Y 0x00010031 |
104 | #define TOUCH_REPORT_USAGE_PG_P 0x000D0030 |
105 | #define TOUCH_REPORT_USAGE_PG_CONTACTID 0x000D0051 |
106 | #define TOUCH_REPORT_USAGE_PG_CONTACTCOUNT 0x000D0054 |
107 | #define TOUCH_REPORT_USAGE_PG_MAJ 0xFF010062 |
108 | #define TOUCH_REPORT_USAGE_PG_MIN 0xFF010063 |
109 | #define TOUCH_COL_USAGE_PG 0x000D0022 |
110 | |
111 | #define SET_CMD_LOW(byte, bits) \ |
112 | ((byte) = (((byte) & 0xF0) | ((bits) & 0x0F))) |
113 | #define SET_CMD_HIGH(byte, bits)\ |
114 | ((byte) = (((byte) & 0x0F) | ((bits) & 0xF0))) |
115 | #define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode) |
116 | #define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4)) |
117 | #define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id) |
118 | |
119 | /* System Information interface definitions */ |
120 | struct cyttsp5_sensing_conf_data_dev { |
121 | u8 electrodes_x; |
122 | u8 electrodes_y; |
123 | __le16 len_x; |
124 | __le16 len_y; |
125 | __le16 res_x; |
126 | __le16 res_y; |
127 | __le16 max_z; |
128 | u8 origin_x; |
129 | u8 origin_y; |
130 | u8 panel_id; |
131 | u8 btn; |
132 | u8 scan_mode; |
133 | u8 max_num_of_tch_per_refresh_cycle; |
134 | } __packed; |
135 | |
136 | struct cyttsp5_sensing_conf_data { |
137 | u16 res_x; |
138 | u16 res_y; |
139 | u16 max_z; |
140 | u16 len_x; |
141 | u16 len_y; |
142 | u8 origin_x; |
143 | u8 origin_y; |
144 | u8 max_tch; |
145 | }; |
146 | |
147 | enum cyttsp5_tch_abs { /* for ordering within the extracted touch data array */ |
148 | CY_TCH_X, /* X */ |
149 | CY_TCH_Y, /* Y */ |
150 | CY_TCH_P, /* P (Z) */ |
151 | CY_TCH_T, /* TOUCH ID */ |
152 | CY_TCH_MAJ, /* TOUCH_MAJOR */ |
153 | CY_TCH_MIN, /* TOUCH_MINOR */ |
154 | CY_TCH_NUM_ABS |
155 | }; |
156 | |
157 | struct cyttsp5_tch_abs_params { |
158 | size_t ofs; /* abs byte offset */ |
159 | size_t size; /* size in bits */ |
160 | size_t min; /* min value */ |
161 | size_t max; /* max value */ |
162 | size_t bofs; /* bit offset */ |
163 | }; |
164 | |
165 | struct cyttsp5_touch { |
166 | int abs[CY_TCH_NUM_ABS]; |
167 | }; |
168 | |
169 | struct cyttsp5_sysinfo { |
170 | struct cyttsp5_sensing_conf_data sensing_conf_data; |
171 | int num_btns; |
172 | struct cyttsp5_tch_abs_params tch_hdr; |
173 | struct cyttsp5_tch_abs_params tch_abs[CY_TCH_NUM_ABS]; |
174 | u32 key_code[HID_SYSINFO_MAX_BTN]; |
175 | }; |
176 | |
177 | struct cyttsp5_hid_desc { |
178 | __le16 hid_desc_len; |
179 | u8 packet_id; |
180 | u8 reserved_byte; |
181 | __le16 bcd_version; |
182 | __le16 report_desc_len; |
183 | __le16 report_desc_register; |
184 | __le16 input_register; |
185 | __le16 max_input_len; |
186 | __le16 output_register; |
187 | __le16 max_output_len; |
188 | __le16 command_register; |
189 | __le16 data_register; |
190 | __le16 vendor_id; |
191 | __le16 product_id; |
192 | __le16 version_id; |
193 | u8 reserved[4]; |
194 | } __packed; |
195 | |
196 | struct cyttsp5 { |
197 | struct device *dev; |
198 | struct completion cmd_done; |
199 | struct cyttsp5_sysinfo sysinfo; |
200 | struct cyttsp5_hid_desc hid_desc; |
201 | u8 cmd_buf[CYTTSP5_PREALLOCATED_CMD_BUFFER]; |
202 | u8 input_buf[CY_MAX_INPUT]; |
203 | u8 response_buf[CY_MAX_INPUT]; |
204 | struct gpio_desc *reset_gpio; |
205 | struct input_dev *input; |
206 | char phys[NAME_MAX]; |
207 | int num_prv_rec; |
208 | struct regmap *regmap; |
209 | struct touchscreen_properties prop; |
210 | struct regulator_bulk_data supplies[2]; |
211 | }; |
212 | |
213 | /* |
214 | * For what is understood in the datasheet, the register does not |
215 | * matter. For consistency, use the Input Register address |
216 | * but it does mean anything to the device. The important data |
217 | * to send is the I2C address |
218 | */ |
219 | static int cyttsp5_read(struct cyttsp5 *ts, u8 *buf, u32 max) |
220 | { |
221 | int error; |
222 | u32 size; |
223 | u8 temp[2]; |
224 | |
225 | /* Read the frame to retrieve the size */ |
226 | error = regmap_bulk_read(map: ts->regmap, HID_INPUT_REG, val: temp, val_count: sizeof(temp)); |
227 | if (error) |
228 | return error; |
229 | |
230 | size = get_unaligned_le16(p: temp); |
231 | if (!size || size == 2) |
232 | return 0; |
233 | |
234 | if (size > max) |
235 | return -EINVAL; |
236 | |
237 | /* Get the real value */ |
238 | return regmap_bulk_read(map: ts->regmap, HID_INPUT_REG, val: buf, val_count: size); |
239 | } |
240 | |
241 | static int cyttsp5_write(struct cyttsp5 *ts, unsigned int reg, u8 *data, |
242 | size_t size) |
243 | { |
244 | u8 cmd[HID_OUTPUT_MAX_CMD_SIZE]; |
245 | |
246 | if (size + 1 > HID_OUTPUT_MAX_CMD_SIZE) |
247 | return -E2BIG; |
248 | |
249 | /* High bytes of register address needed as first byte of cmd */ |
250 | cmd[0] = (reg >> 8) & 0xFF; |
251 | |
252 | /* Copy the rest of the data */ |
253 | if (data) |
254 | memcpy(&cmd[1], data, size); |
255 | |
256 | /* |
257 | * The hardware wants to receive a frame with the address register |
258 | * contained in the first two bytes. As the regmap_write function |
259 | * add the register adresse in the frame, we use the low byte as |
260 | * first frame byte for the address register and the first |
261 | * data byte is the high register + left of the cmd to send |
262 | */ |
263 | return regmap_bulk_write(map: ts->regmap, reg: reg & 0xFF, val: cmd, val_count: size + 1); |
264 | } |
265 | |
266 | static void cyttsp5_get_touch_axis(int *axis, int size, int max, u8 *xy_data, |
267 | int bofs) |
268 | { |
269 | int nbyte; |
270 | |
271 | for (nbyte = 0, *axis = 0; nbyte < size; nbyte++) |
272 | *axis += ((xy_data[nbyte] >> bofs) << (nbyte * 8)); |
273 | |
274 | *axis &= max - 1; |
275 | } |
276 | |
277 | static void cyttsp5_get_touch_record(struct cyttsp5 *ts, |
278 | struct cyttsp5_touch *touch, u8 *xy_data) |
279 | { |
280 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
281 | enum cyttsp5_tch_abs abs; |
282 | |
283 | for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) |
284 | cyttsp5_get_touch_axis(axis: &touch->abs[abs], |
285 | size: si->tch_abs[abs].size, |
286 | max: si->tch_abs[abs].max, |
287 | xy_data: xy_data + si->tch_abs[abs].ofs, |
288 | bofs: si->tch_abs[abs].bofs); |
289 | } |
290 | |
291 | static void cyttsp5_get_mt_touches(struct cyttsp5 *ts, |
292 | struct cyttsp5_touch *tch, int num_cur_tch) |
293 | { |
294 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
295 | int i, t = 0, offset = 0; |
296 | DECLARE_BITMAP(ids, MAX_CY_TCH_T_IDS); |
297 | u8 *tch_addr; |
298 | int tmp; |
299 | |
300 | bitmap_zero(dst: ids, MAX_CY_TCH_T_IDS); |
301 | memset(tch->abs, 0, sizeof(tch->abs)); |
302 | |
303 | switch (ts->input_buf[2]) { |
304 | case HID_TOUCH_REPORT_ID: |
305 | offset = TOUCH_INPUT_HEADER_SIZE; |
306 | break; |
307 | case HID_BTN_REPORT_ID: |
308 | offset = BTN_INPUT_HEADER_SIZE; |
309 | break; |
310 | } |
311 | |
312 | for (i = 0; i < num_cur_tch; i++) { |
313 | tch_addr = ts->input_buf + offset + (i * TOUCH_REPORT_SIZE); |
314 | cyttsp5_get_touch_record(ts, touch: tch, xy_data: tch_addr); |
315 | |
316 | /* Convert MAJOR/MINOR from mm to resolution */ |
317 | tmp = tch->abs[CY_TCH_MAJ] * 100 * si->sensing_conf_data.res_x; |
318 | tch->abs[CY_TCH_MAJ] = tmp / si->sensing_conf_data.len_x; |
319 | tmp = tch->abs[CY_TCH_MIN] * 100 * si->sensing_conf_data.res_x; |
320 | tch->abs[CY_TCH_MIN] = tmp / si->sensing_conf_data.len_x; |
321 | |
322 | t = tch->abs[CY_TCH_T]; |
323 | input_mt_slot(dev: ts->input, slot: t); |
324 | input_mt_report_slot_state(dev: ts->input, MT_TOOL_FINGER, active: true); |
325 | __set_bit(t, ids); |
326 | |
327 | /* position and pressure fields */ |
328 | touchscreen_report_pos(input: ts->input, prop: &ts->prop, |
329 | x: tch->abs[CY_TCH_X], y: tch->abs[CY_TCH_Y], |
330 | multitouch: true); |
331 | input_report_abs(dev: ts->input, ABS_MT_PRESSURE, |
332 | value: tch->abs[CY_TCH_P]); |
333 | |
334 | /* Get the extended touch fields */ |
335 | input_report_abs(dev: ts->input, ABS_MT_TOUCH_MAJOR, |
336 | value: tch->abs[CY_TCH_MAJ]); |
337 | input_report_abs(dev: ts->input, ABS_MT_TOUCH_MINOR, |
338 | value: tch->abs[CY_TCH_MIN]); |
339 | } |
340 | |
341 | ts->num_prv_rec = num_cur_tch; |
342 | } |
343 | |
344 | static int cyttsp5_mt_attention(struct device *dev) |
345 | { |
346 | struct cyttsp5 *ts = dev_get_drvdata(dev); |
347 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
348 | int max_tch = si->sensing_conf_data.max_tch; |
349 | struct cyttsp5_touch tch; |
350 | int num_cur_tch; |
351 | |
352 | cyttsp5_get_touch_axis(axis: &num_cur_tch, size: si->tch_hdr.size, |
353 | max: si->tch_hdr.max, |
354 | xy_data: ts->input_buf + 3 + si->tch_hdr.ofs, |
355 | bofs: si->tch_hdr.bofs); |
356 | |
357 | if (num_cur_tch > max_tch) { |
358 | dev_err(dev, "Num touch err detected (n=%d)\n" , num_cur_tch); |
359 | num_cur_tch = max_tch; |
360 | } |
361 | |
362 | if (num_cur_tch == 0 && ts->num_prv_rec == 0) |
363 | return 0; |
364 | |
365 | /* extract xy_data for all currently reported touches */ |
366 | if (num_cur_tch) |
367 | cyttsp5_get_mt_touches(ts, tch: &tch, num_cur_tch); |
368 | |
369 | input_mt_sync_frame(dev: ts->input); |
370 | input_sync(dev: ts->input); |
371 | |
372 | return 0; |
373 | } |
374 | |
375 | static int cyttsp5_setup_input_device(struct device *dev) |
376 | { |
377 | struct cyttsp5 *ts = dev_get_drvdata(dev); |
378 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
379 | int max_x, max_y, max_p; |
380 | int max_x_tmp, max_y_tmp; |
381 | int error; |
382 | |
383 | max_x_tmp = si->sensing_conf_data.res_x; |
384 | max_y_tmp = si->sensing_conf_data.res_y; |
385 | max_x = max_x_tmp - 1; |
386 | max_y = max_y_tmp - 1; |
387 | max_p = si->sensing_conf_data.max_z; |
388 | |
389 | input_set_abs_params(dev: ts->input, ABS_MT_POSITION_X, min: 0, max: max_x, fuzz: 0, flat: 0); |
390 | input_set_abs_params(dev: ts->input, ABS_MT_POSITION_Y, min: 0, max: max_y, fuzz: 0, flat: 0); |
391 | input_set_abs_params(dev: ts->input, ABS_MT_PRESSURE, min: 0, max: max_p, fuzz: 0, flat: 0); |
392 | |
393 | input_set_abs_params(dev: ts->input, ABS_MT_TOUCH_MAJOR, min: 0, MAX_AREA, fuzz: 0, flat: 0); |
394 | input_set_abs_params(dev: ts->input, ABS_MT_TOUCH_MINOR, min: 0, MAX_AREA, fuzz: 0, flat: 0); |
395 | |
396 | error = input_mt_init_slots(dev: ts->input, num_slots: si->tch_abs[CY_TCH_T].max, |
397 | INPUT_MT_DROP_UNUSED | INPUT_MT_DIRECT); |
398 | if (error) |
399 | return error; |
400 | |
401 | error = input_register_device(ts->input); |
402 | if (error) { |
403 | dev_err(dev, "failed to register input device: %d\n" , error); |
404 | return error; |
405 | } |
406 | |
407 | return error; |
408 | } |
409 | |
410 | static int cyttsp5_parse_dt_key_code(struct device *dev) |
411 | { |
412 | struct cyttsp5 *ts = dev_get_drvdata(dev); |
413 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
414 | |
415 | if (!si->num_btns) |
416 | return 0; |
417 | |
418 | /* Initialize the button to RESERVED */ |
419 | memset32(s: si->key_code, KEY_RESERVED, n: si->num_btns); |
420 | |
421 | return device_property_read_u32_array(dev, propname: "linux,keycodes" , |
422 | val: si->key_code, nval: si->num_btns); |
423 | } |
424 | |
425 | static int cyttsp5_btn_attention(struct device *dev) |
426 | { |
427 | struct cyttsp5 *ts = dev_get_drvdata(dev); |
428 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
429 | int cur_btn, offset = 0; |
430 | int cur_btn_state; |
431 | |
432 | switch (ts->input_buf[2]) { |
433 | case HID_TOUCH_REPORT_ID: |
434 | offset = TOUCH_INPUT_HEADER_SIZE; |
435 | break; |
436 | case HID_BTN_REPORT_ID: |
437 | offset = BTN_INPUT_HEADER_SIZE; |
438 | break; |
439 | } |
440 | |
441 | if (ts->input_buf[2] != HID_BTN_REPORT_ID) |
442 | return 0; |
443 | |
444 | /* extract button press/release touch information */ |
445 | for (cur_btn = 0; cur_btn < si->num_btns; cur_btn++) { |
446 | /* Get current button state */ |
447 | cur_btn_state = (ts->input_buf[offset] >> (cur_btn * CY_BITS_PER_BTN)) |
448 | & CY_NUM_BTN_EVENT_ID; |
449 | |
450 | input_report_key(dev: ts->input, code: si->key_code[cur_btn], |
451 | value: cur_btn_state); |
452 | input_sync(dev: ts->input); |
453 | } |
454 | |
455 | return 0; |
456 | } |
457 | |
458 | static int cyttsp5_validate_cmd_response(struct cyttsp5 *ts, u8 code) |
459 | { |
460 | u16 size, crc; |
461 | u8 status, report_id; |
462 | int command_code; |
463 | |
464 | size = get_unaligned_le16(p: &ts->response_buf[0]); |
465 | if (!size) |
466 | return 0; |
467 | |
468 | report_id = ts->response_buf[HID_OUTPUT_RESPONSE_REPORT_OFFSET]; |
469 | |
470 | switch (report_id) { |
471 | case HID_BL_RESPONSE_REPORT_ID: |
472 | if (ts->response_buf[4] != HID_OUTPUT_BL_SOP) { |
473 | dev_err(ts->dev, "HID output response, wrong SOP\n" ); |
474 | return -EPROTO; |
475 | } |
476 | |
477 | if (ts->response_buf[size - 1] != HID_OUTPUT_BL_EOP) { |
478 | dev_err(ts->dev, "HID output response, wrong EOP\n" ); |
479 | return -EPROTO; |
480 | } |
481 | |
482 | crc = crc_itu_t(crc: 0xFFFF, buffer: &ts->response_buf[4], len: size - 7); |
483 | if (get_unaligned_le16(p: &ts->response_buf[size - 3]) != crc) { |
484 | dev_err(ts->dev, |
485 | "HID output response, wrong CRC 0x%X\n" , |
486 | crc); |
487 | return -EPROTO; |
488 | } |
489 | |
490 | status = ts->response_buf[5]; |
491 | if (status) { |
492 | dev_err(ts->dev, "HID output response, ERROR:%d\n" , |
493 | status); |
494 | return -EPROTO; |
495 | } |
496 | break; |
497 | |
498 | case HID_APP_RESPONSE_REPORT_ID: |
499 | command_code = ts->response_buf[HID_OUTPUT_RESPONSE_CMD_OFFSET] |
500 | & HID_OUTPUT_RESPONSE_CMD_MASK; |
501 | if (command_code != code) { |
502 | dev_err(ts->dev, |
503 | "HID output response, wrong command_code:%X\n" , |
504 | command_code); |
505 | return -EPROTO; |
506 | } |
507 | break; |
508 | } |
509 | |
510 | return 0; |
511 | } |
512 | |
513 | static void cyttsp5_si_get_btn_data(struct cyttsp5 *ts) |
514 | { |
515 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
516 | unsigned int btns = ts->response_buf[HID_SYSINFO_BTN_OFFSET] & |
517 | HID_SYSINFO_BTN_MASK; |
518 | |
519 | si->num_btns = hweight8(btns); |
520 | } |
521 | |
522 | static int cyttsp5_get_sysinfo_regs(struct cyttsp5 *ts) |
523 | { |
524 | struct cyttsp5_sensing_conf_data *scd = &ts->sysinfo.sensing_conf_data; |
525 | struct cyttsp5_sensing_conf_data_dev *scd_dev = |
526 | (struct cyttsp5_sensing_conf_data_dev *) |
527 | &ts->response_buf[HID_SYSINFO_SENSING_OFFSET]; |
528 | |
529 | cyttsp5_si_get_btn_data(ts); |
530 | |
531 | scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle; |
532 | scd->res_x = get_unaligned_le16(p: &scd_dev->res_x); |
533 | scd->res_y = get_unaligned_le16(p: &scd_dev->res_y); |
534 | scd->max_z = get_unaligned_le16(p: &scd_dev->max_z); |
535 | scd->len_x = get_unaligned_le16(p: &scd_dev->len_x); |
536 | scd->len_y = get_unaligned_le16(p: &scd_dev->len_y); |
537 | |
538 | return 0; |
539 | } |
540 | |
541 | static int cyttsp5_hid_output_get_sysinfo(struct cyttsp5 *ts) |
542 | { |
543 | int rc; |
544 | u8 cmd[HID_OUTPUT_GET_SYSINFO_SIZE]; |
545 | |
546 | /* HI bytes of Output register address */ |
547 | put_unaligned_le16(HID_OUTPUT_GET_SYSINFO_SIZE, p: cmd); |
548 | cmd[2] = HID_APP_OUTPUT_REPORT_ID; |
549 | cmd[3] = 0x0; /* Reserved */ |
550 | cmd[4] = HID_OUTPUT_GET_SYSINFO; |
551 | |
552 | rc = cyttsp5_write(ts, HID_OUTPUT_REG, data: cmd, |
553 | HID_OUTPUT_GET_SYSINFO_SIZE); |
554 | if (rc) { |
555 | dev_err(ts->dev, "Failed to write command %d" , rc); |
556 | return rc; |
557 | } |
558 | |
559 | rc = wait_for_completion_interruptible_timeout(x: &ts->cmd_done, |
560 | timeout: msecs_to_jiffies(CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT_MS)); |
561 | if (rc <= 0) { |
562 | dev_err(ts->dev, "HID output cmd execution timed out\n" ); |
563 | rc = -ETIMEDOUT; |
564 | return rc; |
565 | } |
566 | |
567 | rc = cyttsp5_validate_cmd_response(ts, HID_OUTPUT_GET_SYSINFO); |
568 | if (rc) { |
569 | dev_err(ts->dev, "Validation of the response failed\n" ); |
570 | return rc; |
571 | } |
572 | |
573 | return cyttsp5_get_sysinfo_regs(ts); |
574 | } |
575 | |
576 | static int cyttsp5_power_control(struct cyttsp5 *ts, bool on) |
577 | { |
578 | u8 state = on ? HID_POWER_ON : HID_POWER_SLEEP; |
579 | u8 cmd[2] = { 0 }; |
580 | int rc; |
581 | |
582 | SET_CMD_REPORT_TYPE(cmd[0], 0); |
583 | SET_CMD_REPORT_ID(cmd[0], HID_POWER_SLEEP); |
584 | SET_CMD_OPCODE(cmd[1], HID_CMD_SET_POWER); |
585 | |
586 | rc = cyttsp5_write(ts, HID_COMMAND_REG, data: cmd, size: sizeof(cmd)); |
587 | if (rc) { |
588 | dev_err(ts->dev, "Failed to write power command %d" , rc); |
589 | return rc; |
590 | } |
591 | |
592 | rc = wait_for_completion_interruptible_timeout(x: &ts->cmd_done, |
593 | timeout: msecs_to_jiffies(CY_HID_SET_POWER_TIMEOUT)); |
594 | if (rc <= 0) { |
595 | dev_err(ts->dev, "HID power cmd execution timed out\n" ); |
596 | return -ETIMEDOUT; |
597 | } |
598 | |
599 | if (ts->response_buf[2] != HID_RESPONSE_REPORT_ID || |
600 | (ts->response_buf[3] & 0x03) != state || |
601 | (ts->response_buf[4] & 0x0f) != HID_CMD_SET_POWER) { |
602 | dev_err(ts->dev, "Validation of the %s response failed\n" , |
603 | on ? "wakeup" : "sleep" ); |
604 | return -EINVAL; |
605 | } |
606 | |
607 | return 0; |
608 | } |
609 | |
610 | static int cyttsp5_hid_output_bl_launch_app(struct cyttsp5 *ts) |
611 | { |
612 | int rc; |
613 | u8 cmd[HID_OUTPUT_BL_LAUNCH_APP_SIZE]; |
614 | u16 crc; |
615 | |
616 | put_unaligned_le16(HID_OUTPUT_BL_LAUNCH_APP_SIZE, p: cmd); |
617 | cmd[2] = HID_BL_OUTPUT_REPORT_ID; |
618 | cmd[3] = 0x0; /* Reserved */ |
619 | cmd[4] = HID_OUTPUT_BL_SOP; |
620 | cmd[5] = HID_OUTPUT_BL_LAUNCH_APP; |
621 | put_unaligned_le16(val: 0x00, p: &cmd[6]); |
622 | crc = crc_itu_t(crc: 0xFFFF, buffer: &cmd[4], len: 4); |
623 | put_unaligned_le16(val: crc, p: &cmd[8]); |
624 | cmd[10] = HID_OUTPUT_BL_EOP; |
625 | |
626 | rc = cyttsp5_write(ts, HID_OUTPUT_REG, data: cmd, |
627 | HID_OUTPUT_BL_LAUNCH_APP_SIZE); |
628 | if (rc) { |
629 | dev_err(ts->dev, "Failed to write command %d" , rc); |
630 | return rc; |
631 | } |
632 | |
633 | rc = wait_for_completion_interruptible_timeout(x: &ts->cmd_done, |
634 | timeout: msecs_to_jiffies(CY_HID_OUTPUT_TIMEOUT_MS)); |
635 | if (rc <= 0) { |
636 | dev_err(ts->dev, "HID output cmd execution timed out\n" ); |
637 | rc = -ETIMEDOUT; |
638 | return rc; |
639 | } |
640 | |
641 | rc = cyttsp5_validate_cmd_response(ts, HID_OUTPUT_BL_LAUNCH_APP); |
642 | if (rc) { |
643 | dev_err(ts->dev, "Validation of the response failed\n" ); |
644 | return rc; |
645 | } |
646 | |
647 | return 0; |
648 | } |
649 | |
650 | static int cyttsp5_get_hid_descriptor(struct cyttsp5 *ts, |
651 | struct cyttsp5_hid_desc *desc) |
652 | { |
653 | struct device *dev = ts->dev; |
654 | int rc; |
655 | |
656 | rc = cyttsp5_write(ts, HID_DESC_REG, NULL, size: 0); |
657 | if (rc) { |
658 | dev_err(dev, "Failed to get HID descriptor, rc=%d\n" , rc); |
659 | return rc; |
660 | } |
661 | |
662 | rc = wait_for_completion_interruptible_timeout(x: &ts->cmd_done, |
663 | timeout: msecs_to_jiffies(CY_HID_GET_HID_DESCRIPTOR_TIMEOUT_MS)); |
664 | if (rc <= 0) { |
665 | dev_err(ts->dev, "HID get descriptor timed out\n" ); |
666 | rc = -ETIMEDOUT; |
667 | return rc; |
668 | } |
669 | |
670 | memcpy(desc, ts->response_buf, sizeof(*desc)); |
671 | |
672 | /* Check HID descriptor length and version */ |
673 | if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) || |
674 | le16_to_cpu(desc->bcd_version) != HID_VERSION) { |
675 | dev_err(dev, "Unsupported HID version\n" ); |
676 | return -ENODEV; |
677 | } |
678 | |
679 | return 0; |
680 | } |
681 | |
682 | static int fill_tch_abs(struct cyttsp5_tch_abs_params *tch_abs, int report_size, |
683 | int offset) |
684 | { |
685 | tch_abs->ofs = offset / 8; |
686 | tch_abs->size = report_size / 8; |
687 | if (report_size % 8) |
688 | tch_abs->size += 1; |
689 | tch_abs->min = 0; |
690 | tch_abs->max = 1 << report_size; |
691 | tch_abs->bofs = offset - (tch_abs->ofs << 3); |
692 | |
693 | return 0; |
694 | } |
695 | |
696 | static irqreturn_t cyttsp5_handle_irq(int irq, void *handle) |
697 | { |
698 | struct cyttsp5 *ts = handle; |
699 | int report_id; |
700 | int size; |
701 | int error; |
702 | |
703 | error = cyttsp5_read(ts, buf: ts->input_buf, CY_MAX_INPUT); |
704 | if (error) |
705 | return IRQ_HANDLED; |
706 | |
707 | size = get_unaligned_le16(p: &ts->input_buf[0]); |
708 | if (size == 0) { |
709 | /* reset */ |
710 | report_id = 0; |
711 | size = 2; |
712 | } else { |
713 | report_id = ts->input_buf[2]; |
714 | } |
715 | |
716 | switch (report_id) { |
717 | case HID_TOUCH_REPORT_ID: |
718 | cyttsp5_mt_attention(dev: ts->dev); |
719 | break; |
720 | case HID_BTN_REPORT_ID: |
721 | cyttsp5_btn_attention(dev: ts->dev); |
722 | break; |
723 | case HID_RESPONSE_REPORT_ID: |
724 | memcpy(ts->response_buf, ts->input_buf, size); |
725 | complete(&ts->cmd_done); |
726 | break; |
727 | default: |
728 | /* It is not an input but a command response */ |
729 | memcpy(ts->response_buf, ts->input_buf, size); |
730 | complete(&ts->cmd_done); |
731 | } |
732 | |
733 | return IRQ_HANDLED; |
734 | } |
735 | |
736 | static int cyttsp5_deassert_int(struct cyttsp5 *ts) |
737 | { |
738 | u16 size; |
739 | u8 buf[2]; |
740 | int error; |
741 | |
742 | error = regmap_bulk_read(map: ts->regmap, HID_INPUT_REG, val: buf, val_count: sizeof(buf)); |
743 | if (error < 0) |
744 | return error; |
745 | |
746 | size = get_unaligned_le16(p: &buf[0]); |
747 | if (size == 2 || size == 0) |
748 | return 0; |
749 | |
750 | return -EINVAL; |
751 | } |
752 | |
753 | static int cyttsp5_fill_all_touch(struct cyttsp5 *ts) |
754 | { |
755 | struct cyttsp5_sysinfo *si = &ts->sysinfo; |
756 | |
757 | fill_tch_abs(tch_abs: &si->tch_abs[CY_TCH_X], REPORT_SIZE_16, |
758 | TOUCH_REPORT_DESC_X); |
759 | fill_tch_abs(tch_abs: &si->tch_abs[CY_TCH_Y], REPORT_SIZE_16, |
760 | TOUCH_REPORT_DESC_Y); |
761 | fill_tch_abs(tch_abs: &si->tch_abs[CY_TCH_P], REPORT_SIZE_8, |
762 | TOUCH_REPORT_DESC_P); |
763 | fill_tch_abs(tch_abs: &si->tch_abs[CY_TCH_T], REPORT_SIZE_5, |
764 | TOUCH_REPORT_DESC_CONTACTID); |
765 | fill_tch_abs(tch_abs: &si->tch_hdr, REPORT_SIZE_5, |
766 | TOUCH_REPORT_DESC_HDR_CONTACTCOUNT); |
767 | fill_tch_abs(tch_abs: &si->tch_abs[CY_TCH_MAJ], REPORT_SIZE_8, |
768 | TOUCH_REPORT_DESC_MAJ); |
769 | fill_tch_abs(tch_abs: &si->tch_abs[CY_TCH_MIN], REPORT_SIZE_8, |
770 | TOUCH_REPORT_DESC_MIN); |
771 | |
772 | return 0; |
773 | } |
774 | |
775 | static int cyttsp5_startup(struct cyttsp5 *ts) |
776 | { |
777 | int error; |
778 | |
779 | error = cyttsp5_deassert_int(ts); |
780 | if (error) { |
781 | dev_err(ts->dev, "Error on deassert int r=%d\n" , error); |
782 | return -ENODEV; |
783 | } |
784 | |
785 | /* |
786 | * Launch the application as the device starts in bootloader mode |
787 | * because of a power-on-reset |
788 | */ |
789 | error = cyttsp5_hid_output_bl_launch_app(ts); |
790 | if (error < 0) { |
791 | dev_err(ts->dev, "Error on launch app r=%d\n" , error); |
792 | return error; |
793 | } |
794 | |
795 | error = cyttsp5_get_hid_descriptor(ts, desc: &ts->hid_desc); |
796 | if (error < 0) { |
797 | dev_err(ts->dev, "Error on getting HID descriptor r=%d\n" , error); |
798 | return error; |
799 | } |
800 | |
801 | error = cyttsp5_fill_all_touch(ts); |
802 | if (error < 0) { |
803 | dev_err(ts->dev, "Error on report descriptor r=%d\n" , error); |
804 | return error; |
805 | } |
806 | |
807 | error = cyttsp5_hid_output_get_sysinfo(ts); |
808 | if (error) { |
809 | dev_err(ts->dev, "Error on getting sysinfo r=%d\n" , error); |
810 | return error; |
811 | } |
812 | |
813 | return error; |
814 | } |
815 | |
816 | static void cyttsp5_cleanup(void *data) |
817 | { |
818 | struct cyttsp5 *ts = data; |
819 | |
820 | regulator_bulk_disable(ARRAY_SIZE(ts->supplies), consumers: ts->supplies); |
821 | } |
822 | |
823 | static int cyttsp5_probe(struct device *dev, struct regmap *regmap, int irq, |
824 | const char *name) |
825 | { |
826 | struct cyttsp5 *ts; |
827 | struct cyttsp5_sysinfo *si; |
828 | int error, i; |
829 | |
830 | ts = devm_kzalloc(dev, size: sizeof(*ts), GFP_KERNEL); |
831 | if (!ts) |
832 | return -ENOMEM; |
833 | |
834 | /* Initialize device info */ |
835 | ts->regmap = regmap; |
836 | ts->dev = dev; |
837 | si = &ts->sysinfo; |
838 | dev_set_drvdata(dev, data: ts); |
839 | |
840 | init_completion(x: &ts->cmd_done); |
841 | |
842 | /* Power up the device */ |
843 | ts->supplies[0].supply = "vdd" ; |
844 | ts->supplies[1].supply = "vddio" ; |
845 | error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->supplies), |
846 | consumers: ts->supplies); |
847 | if (error) { |
848 | dev_err(ts->dev, "Failed to get regulators, error %d\n" , error); |
849 | return error; |
850 | } |
851 | |
852 | error = devm_add_action_or_reset(dev, cyttsp5_cleanup, ts); |
853 | if (error) |
854 | return error; |
855 | |
856 | error = regulator_bulk_enable(ARRAY_SIZE(ts->supplies), consumers: ts->supplies); |
857 | if (error) { |
858 | dev_err(ts->dev, "Failed to enable regulators, error %d\n" , error); |
859 | return error; |
860 | } |
861 | |
862 | ts->input = devm_input_allocate_device(dev); |
863 | if (!ts->input) { |
864 | dev_err(dev, "Error, failed to allocate input device\n" ); |
865 | return -ENODEV; |
866 | } |
867 | |
868 | ts->input->name = "cyttsp5" ; |
869 | scnprintf(buf: ts->phys, size: sizeof(ts->phys), fmt: "%s/input0" , dev_name(dev)); |
870 | ts->input->phys = ts->phys; |
871 | input_set_drvdata(dev: ts->input, data: ts); |
872 | |
873 | /* Reset the gpio to be in a reset state */ |
874 | ts->reset_gpio = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
875 | if (IS_ERR(ptr: ts->reset_gpio)) { |
876 | error = PTR_ERR(ptr: ts->reset_gpio); |
877 | dev_err(dev, "Failed to request reset gpio, error %d\n" , error); |
878 | return error; |
879 | } |
880 | gpiod_set_value_cansleep(desc: ts->reset_gpio, value: 0); |
881 | |
882 | /* Need a delay to have device up */ |
883 | msleep(msecs: 20); |
884 | |
885 | error = devm_request_threaded_irq(dev, irq, NULL, thread_fn: cyttsp5_handle_irq, |
886 | IRQF_ONESHOT, devname: name, dev_id: ts); |
887 | if (error) { |
888 | dev_err(dev, "unable to request IRQ\n" ); |
889 | return error; |
890 | } |
891 | |
892 | error = cyttsp5_startup(ts); |
893 | if (error) { |
894 | dev_err(ts->dev, "Fail initial startup r=%d\n" , error); |
895 | return error; |
896 | } |
897 | |
898 | error = cyttsp5_parse_dt_key_code(dev); |
899 | if (error < 0) { |
900 | dev_err(ts->dev, "Error while parsing dts %d\n" , error); |
901 | return error; |
902 | } |
903 | |
904 | touchscreen_parse_properties(input: ts->input, multitouch: true, prop: &ts->prop); |
905 | |
906 | __set_bit(EV_KEY, ts->input->evbit); |
907 | for (i = 0; i < si->num_btns; i++) |
908 | __set_bit(si->key_code[i], ts->input->keybit); |
909 | |
910 | return cyttsp5_setup_input_device(dev); |
911 | } |
912 | |
913 | static int cyttsp5_i2c_probe(struct i2c_client *client) |
914 | { |
915 | struct regmap *regmap; |
916 | static const struct regmap_config config = { |
917 | .reg_bits = 8, |
918 | .val_bits = 8, |
919 | }; |
920 | |
921 | regmap = devm_regmap_init_i2c(client, &config); |
922 | if (IS_ERR(ptr: regmap)) { |
923 | dev_err(&client->dev, "regmap allocation failed: %ld\n" , |
924 | PTR_ERR(regmap)); |
925 | return PTR_ERR(ptr: regmap); |
926 | } |
927 | |
928 | return cyttsp5_probe(dev: &client->dev, regmap, irq: client->irq, name: client->name); |
929 | } |
930 | |
931 | static const struct of_device_id cyttsp5_of_match[] = { |
932 | { .compatible = "cypress,tt21000" , }, |
933 | { } |
934 | }; |
935 | MODULE_DEVICE_TABLE(of, cyttsp5_of_match); |
936 | |
937 | static const struct i2c_device_id cyttsp5_i2c_id[] = { |
938 | { CYTTSP5_NAME, 0, }, |
939 | { } |
940 | }; |
941 | MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id); |
942 | |
943 | static int __maybe_unused cyttsp5_suspend(struct device *dev) |
944 | { |
945 | struct cyttsp5 *ts = dev_get_drvdata(dev); |
946 | |
947 | if (!device_may_wakeup(dev)) |
948 | cyttsp5_power_control(ts, on: false); |
949 | |
950 | return 0; |
951 | } |
952 | |
953 | static int __maybe_unused cyttsp5_resume(struct device *dev) |
954 | { |
955 | struct cyttsp5 *ts = dev_get_drvdata(dev); |
956 | |
957 | if (!device_may_wakeup(dev)) |
958 | cyttsp5_power_control(ts, on: true); |
959 | |
960 | return 0; |
961 | } |
962 | |
963 | static SIMPLE_DEV_PM_OPS(cyttsp5_pm, cyttsp5_suspend, cyttsp5_resume); |
964 | |
965 | static struct i2c_driver cyttsp5_i2c_driver = { |
966 | .driver = { |
967 | .name = CYTTSP5_NAME, |
968 | .of_match_table = cyttsp5_of_match, |
969 | .pm = &cyttsp5_pm, |
970 | }, |
971 | .probe = cyttsp5_i2c_probe, |
972 | .id_table = cyttsp5_i2c_id, |
973 | }; |
974 | module_i2c_driver(cyttsp5_i2c_driver); |
975 | |
976 | MODULE_LICENSE("GPL" ); |
977 | MODULE_DESCRIPTION("Touchscreen driver for Cypress TrueTouch Gen 5 Product" ); |
978 | MODULE_AUTHOR("Mylène Josserand <mylene.josserand@bootlin.com>" ); |
979 | |