1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021
4 * Author(s): Giulio Benetti <giulio.benetti@benettiengineering.com>
5 */
6
7#include <linux/delay.h>
8#include <linux/gpio/consumer.h>
9#include <linux/i2c.h>
10#include <linux/interrupt.h>
11#include <linux/input.h>
12#include <linux/input/mt.h>
13#include <linux/input/touchscreen.h>
14#include <linux/irq.h>
15#include <linux/regulator/consumer.h>
16#include <linux/regmap.h>
17
18#include <asm/unaligned.h>
19
20#define HY46XX_CHKSUM_CODE		0x1
21#define HY46XX_FINGER_NUM		0x2
22#define HY46XX_CHKSUM_LEN		0x7
23#define HY46XX_THRESHOLD		0x80
24#define HY46XX_GLOVE_EN			0x84
25#define HY46XX_REPORT_SPEED		0x88
26#define HY46XX_PWR_NOISE_EN		0x89
27#define HY46XX_FILTER_DATA		0x8A
28#define HY46XX_GAIN			0x92
29#define HY46XX_EDGE_OFFSET		0x93
30#define HY46XX_RX_NR_USED		0x94
31#define HY46XX_TX_NR_USED		0x95
32#define HY46XX_PWR_MODE			0xA5
33#define HY46XX_FW_VERSION		0xA6
34#define HY46XX_LIB_VERSION		0xA7
35#define HY46XX_TP_INFO			0xA8
36#define HY46XX_TP_CHIP_ID		0xA9
37#define HY46XX_BOOT_VER			0xB0
38
39#define HY46XX_TPLEN			0x6
40#define HY46XX_REPORT_PKT_LEN		0x44
41
42#define HY46XX_MAX_SUPPORTED_POINTS	11
43
44#define TOUCH_EVENT_DOWN		0x00
45#define TOUCH_EVENT_UP			0x01
46#define TOUCH_EVENT_CONTACT		0x02
47#define TOUCH_EVENT_RESERVED		0x03
48
49struct hycon_hy46xx_data {
50	struct i2c_client *client;
51	struct input_dev *input;
52	struct touchscreen_properties prop;
53	struct regulator *vcc;
54
55	struct gpio_desc *reset_gpio;
56
57	struct mutex mutex;
58	struct regmap *regmap;
59
60	int threshold;
61	bool glove_enable;
62	int report_speed;
63	bool noise_filter_enable;
64	int filter_data;
65	int gain;
66	int edge_offset;
67	int rx_number_used;
68	int tx_number_used;
69	int power_mode;
70	int fw_version;
71	int lib_version;
72	int tp_information;
73	int tp_chip_id;
74	int bootloader_version;
75};
76
77static const struct regmap_config hycon_hy46xx_i2c_regmap_config = {
78	.reg_bits = 8,
79	.val_bits = 8,
80};
81
82static bool hycon_hy46xx_check_checksum(struct hycon_hy46xx_data *tsdata, u8 *buf)
83{
84	u8 chksum = 0;
85	int i;
86
87	for (i = 2; i < buf[HY46XX_CHKSUM_LEN]; i++)
88		chksum += buf[i];
89
90	if (chksum == buf[HY46XX_CHKSUM_CODE])
91		return true;
92
93	dev_err_ratelimited(&tsdata->client->dev,
94			    "checksum error: 0x%02x expected, got 0x%02x\n",
95			    chksum, buf[HY46XX_CHKSUM_CODE]);
96
97	return false;
98}
99
100static irqreturn_t hycon_hy46xx_isr(int irq, void *dev_id)
101{
102	struct hycon_hy46xx_data *tsdata = dev_id;
103	struct device *dev = &tsdata->client->dev;
104	u8 rdbuf[HY46XX_REPORT_PKT_LEN];
105	int i, x, y, id;
106	int error;
107
108	memset(rdbuf, 0, sizeof(rdbuf));
109
110	error = regmap_bulk_read(tsdata->regmap, 0, rdbuf, sizeof(rdbuf));
111	if (error) {
112		dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n",
113				    error);
114		goto out;
115	}
116
117	if (!hycon_hy46xx_check_checksum(tsdata, rdbuf))
118		goto out;
119
120	for (i = 0; i < HY46XX_MAX_SUPPORTED_POINTS; i++) {
121		u8 *buf = &rdbuf[3 + (HY46XX_TPLEN * i)];
122		int type = buf[0] >> 6;
123
124		if (type == TOUCH_EVENT_RESERVED)
125			continue;
126
127		x = get_unaligned_be16(buf) & 0x0fff;
128		y = get_unaligned_be16(buf + 2) & 0x0fff;
129
130		id = buf[2] >> 4;
131
132		input_mt_slot(tsdata->input, id);
133		if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER,
134					       type != TOUCH_EVENT_UP))
135			touchscreen_report_pos(tsdata->input, &tsdata->prop,
136					       x, y, true);
137	}
138
139	input_mt_report_pointer_emulation(tsdata->input, false);
140	input_sync(tsdata->input);
141
142out:
143	return IRQ_HANDLED;
144}
145
146struct hycon_hy46xx_attribute {
147	struct device_attribute dattr;
148	size_t field_offset;
149	u8 address;
150	u8 limit_low;
151	u8 limit_high;
152};
153
154#define HYCON_ATTR_U8(_field, _mode, _address, _limit_low, _limit_high)	\
155	struct hycon_hy46xx_attribute hycon_hy46xx_attr_##_field = {		\
156		.dattr = __ATTR(_field, _mode,				\
157				hycon_hy46xx_setting_show,			\
158				hycon_hy46xx_setting_store),			\
159		.field_offset = offsetof(struct hycon_hy46xx_data, _field),	\
160		.address = _address,					\
161		.limit_low = _limit_low,				\
162		.limit_high = _limit_high,				\
163	}
164
165#define HYCON_ATTR_BOOL(_field, _mode, _address)			\
166	struct hycon_hy46xx_attribute hycon_hy46xx_attr_##_field = {		\
167		.dattr = __ATTR(_field, _mode,				\
168				hycon_hy46xx_setting_show,			\
169				hycon_hy46xx_setting_store),			\
170		.field_offset = offsetof(struct hycon_hy46xx_data, _field),	\
171		.address = _address,					\
172		.limit_low = false,					\
173		.limit_high = true,					\
174	}
175
176static ssize_t hycon_hy46xx_setting_show(struct device *dev,
177				   struct device_attribute *dattr, char *buf)
178{
179	struct i2c_client *client = to_i2c_client(dev);
180	struct hycon_hy46xx_data *tsdata = i2c_get_clientdata(client);
181	struct hycon_hy46xx_attribute *attr =
182			container_of(dattr, struct hycon_hy46xx_attribute, dattr);
183	u8 *field = (u8 *)tsdata + attr->field_offset;
184	size_t count = 0;
185	int error = 0;
186	int val;
187
188	mutex_lock(&tsdata->mutex);
189
190	error = regmap_read(tsdata->regmap, attr->address, &val);
191	if (error < 0) {
192		dev_err(&tsdata->client->dev,
193			"Failed to fetch attribute %s, error %d\n",
194			dattr->attr.name, error);
195		goto out;
196	}
197
198	if (val != *field) {
199		dev_warn(&tsdata->client->dev,
200			 "%s: read (%d) and stored value (%d) differ\n",
201			 dattr->attr.name, val, *field);
202		*field = val;
203	}
204
205	count = sysfs_emit(buf, "%d\n", val);
206
207out:
208	mutex_unlock(&tsdata->mutex);
209	return error ?: count;
210}
211
212static ssize_t hycon_hy46xx_setting_store(struct device *dev,
213					struct device_attribute *dattr,
214					const char *buf, size_t count)
215{
216	struct i2c_client *client = to_i2c_client(dev);
217	struct hycon_hy46xx_data *tsdata = i2c_get_clientdata(client);
218	struct hycon_hy46xx_attribute *attr =
219			container_of(dattr, struct hycon_hy46xx_attribute, dattr);
220	u8 *field = (u8 *)tsdata + attr->field_offset;
221	unsigned int val;
222	int error;
223
224	mutex_lock(&tsdata->mutex);
225
226	error = kstrtouint(buf, 0, &val);
227	if (error)
228		goto out;
229
230	if (val < attr->limit_low || val > attr->limit_high) {
231		error = -ERANGE;
232		goto out;
233	}
234
235	error = regmap_write(tsdata->regmap, attr->address, val);
236	if (error < 0) {
237		dev_err(&tsdata->client->dev,
238			"Failed to update attribute %s, error: %d\n",
239			dattr->attr.name, error);
240		goto out;
241	}
242	*field = val;
243
244out:
245	mutex_unlock(&tsdata->mutex);
246	return error ?: count;
247}
248
249static HYCON_ATTR_U8(threshold, 0644, HY46XX_THRESHOLD, 0, 255);
250static HYCON_ATTR_BOOL(glove_enable, 0644, HY46XX_GLOVE_EN);
251static HYCON_ATTR_U8(report_speed, 0644, HY46XX_REPORT_SPEED, 0, 255);
252static HYCON_ATTR_BOOL(noise_filter_enable, 0644, HY46XX_PWR_NOISE_EN);
253static HYCON_ATTR_U8(filter_data, 0644, HY46XX_FILTER_DATA, 0, 5);
254static HYCON_ATTR_U8(gain, 0644, HY46XX_GAIN, 0, 5);
255static HYCON_ATTR_U8(edge_offset, 0644, HY46XX_EDGE_OFFSET, 0, 5);
256static HYCON_ATTR_U8(fw_version, 0444, HY46XX_FW_VERSION, 0, 255);
257static HYCON_ATTR_U8(lib_version, 0444, HY46XX_LIB_VERSION, 0, 255);
258static HYCON_ATTR_U8(tp_information, 0444, HY46XX_TP_INFO, 0, 255);
259static HYCON_ATTR_U8(tp_chip_id, 0444, HY46XX_TP_CHIP_ID, 0, 255);
260static HYCON_ATTR_U8(bootloader_version, 0444, HY46XX_BOOT_VER, 0, 255);
261
262static struct attribute *hycon_hy46xx_attrs[] = {
263	&hycon_hy46xx_attr_threshold.dattr.attr,
264	&hycon_hy46xx_attr_glove_enable.dattr.attr,
265	&hycon_hy46xx_attr_report_speed.dattr.attr,
266	&hycon_hy46xx_attr_noise_filter_enable.dattr.attr,
267	&hycon_hy46xx_attr_filter_data.dattr.attr,
268	&hycon_hy46xx_attr_gain.dattr.attr,
269	&hycon_hy46xx_attr_edge_offset.dattr.attr,
270	&hycon_hy46xx_attr_fw_version.dattr.attr,
271	&hycon_hy46xx_attr_lib_version.dattr.attr,
272	&hycon_hy46xx_attr_tp_information.dattr.attr,
273	&hycon_hy46xx_attr_tp_chip_id.dattr.attr,
274	&hycon_hy46xx_attr_bootloader_version.dattr.attr,
275	NULL
276};
277ATTRIBUTE_GROUPS(hycon_hy46xx);
278
279static void hycon_hy46xx_get_defaults(struct device *dev, struct hycon_hy46xx_data *tsdata)
280{
281	bool val_bool;
282	int error;
283	u32 val;
284
285	error = device_property_read_u32(dev, "hycon,threshold", &val);
286	if (!error) {
287		error = regmap_write(tsdata->regmap, HY46XX_THRESHOLD, val);
288		if (error < 0)
289			goto out;
290
291		tsdata->threshold = val;
292	}
293
294	val_bool = device_property_read_bool(dev, "hycon,glove-enable");
295	error = regmap_write(tsdata->regmap, HY46XX_GLOVE_EN, val_bool);
296	if (error < 0)
297		goto out;
298	tsdata->glove_enable = val_bool;
299
300	error = device_property_read_u32(dev, "hycon,report-speed-hz", &val);
301	if (!error) {
302		error = regmap_write(tsdata->regmap, HY46XX_REPORT_SPEED, val);
303		if (error < 0)
304			goto out;
305
306		tsdata->report_speed = val;
307	}
308
309	val_bool = device_property_read_bool(dev, "hycon,noise-filter-enable");
310	error = regmap_write(tsdata->regmap, HY46XX_PWR_NOISE_EN, val_bool);
311	if (error < 0)
312		goto out;
313	tsdata->noise_filter_enable = val_bool;
314
315	error = device_property_read_u32(dev, "hycon,filter-data", &val);
316	if (!error) {
317		error = regmap_write(tsdata->regmap, HY46XX_FILTER_DATA, val);
318		if (error < 0)
319			goto out;
320
321		tsdata->filter_data = val;
322	}
323
324	error = device_property_read_u32(dev, "hycon,gain", &val);
325	if (!error) {
326		error = regmap_write(tsdata->regmap, HY46XX_GAIN, val);
327		if (error < 0)
328			goto out;
329
330		tsdata->gain = val;
331	}
332
333	error = device_property_read_u32(dev, "hycon,edge-offset", &val);
334	if (!error) {
335		error = regmap_write(tsdata->regmap, HY46XX_EDGE_OFFSET, val);
336		if (error < 0)
337			goto out;
338
339		tsdata->edge_offset = val;
340	}
341
342	return;
343out:
344	dev_err(&tsdata->client->dev, "Failed to set default settings");
345}
346
347static void hycon_hy46xx_get_parameters(struct hycon_hy46xx_data *tsdata)
348{
349	int error;
350	u32 val;
351
352	error = regmap_read(tsdata->regmap, HY46XX_THRESHOLD, &val);
353	if (error < 0)
354		goto out;
355	tsdata->threshold = val;
356
357	error = regmap_read(tsdata->regmap, HY46XX_GLOVE_EN, &val);
358	if (error < 0)
359		goto out;
360	tsdata->glove_enable = val;
361
362	error = regmap_read(tsdata->regmap, HY46XX_REPORT_SPEED, &val);
363	if (error < 0)
364		goto out;
365	tsdata->report_speed = val;
366
367	error = regmap_read(tsdata->regmap, HY46XX_PWR_NOISE_EN, &val);
368	if (error < 0)
369		goto out;
370	tsdata->noise_filter_enable = val;
371
372	error = regmap_read(tsdata->regmap, HY46XX_FILTER_DATA, &val);
373	if (error < 0)
374		goto out;
375	tsdata->filter_data = val;
376
377	error = regmap_read(tsdata->regmap, HY46XX_GAIN, &val);
378	if (error < 0)
379		goto out;
380	tsdata->gain = val;
381
382	error = regmap_read(tsdata->regmap, HY46XX_EDGE_OFFSET, &val);
383	if (error < 0)
384		goto out;
385	tsdata->edge_offset = val;
386
387	error = regmap_read(tsdata->regmap, HY46XX_RX_NR_USED, &val);
388	if (error < 0)
389		goto out;
390	tsdata->rx_number_used = val;
391
392	error = regmap_read(tsdata->regmap, HY46XX_TX_NR_USED, &val);
393	if (error < 0)
394		goto out;
395	tsdata->tx_number_used = val;
396
397	error = regmap_read(tsdata->regmap, HY46XX_PWR_MODE, &val);
398	if (error < 0)
399		goto out;
400	tsdata->power_mode = val;
401
402	error = regmap_read(tsdata->regmap, HY46XX_FW_VERSION, &val);
403	if (error < 0)
404		goto out;
405	tsdata->fw_version = val;
406
407	error = regmap_read(tsdata->regmap, HY46XX_LIB_VERSION, &val);
408	if (error < 0)
409		goto out;
410	tsdata->lib_version = val;
411
412	error = regmap_read(tsdata->regmap, HY46XX_TP_INFO, &val);
413	if (error < 0)
414		goto out;
415	tsdata->tp_information = val;
416
417	error = regmap_read(tsdata->regmap, HY46XX_TP_CHIP_ID, &val);
418	if (error < 0)
419		goto out;
420	tsdata->tp_chip_id = val;
421
422	error = regmap_read(tsdata->regmap, HY46XX_BOOT_VER, &val);
423	if (error < 0)
424		goto out;
425	tsdata->bootloader_version = val;
426
427	return;
428out:
429	dev_err(&tsdata->client->dev, "Failed to read default settings");
430}
431
432static void hycon_hy46xx_disable_regulator(void *arg)
433{
434	struct hycon_hy46xx_data *data = arg;
435
436	regulator_disable(data->vcc);
437}
438
439static int hycon_hy46xx_probe(struct i2c_client *client)
440{
441	struct hycon_hy46xx_data *tsdata;
442	struct input_dev *input;
443	int error;
444
445	dev_dbg(&client->dev, "probing for HYCON HY46XX I2C\n");
446
447	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
448	if (!tsdata)
449		return -ENOMEM;
450
451	tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
452	if (IS_ERR(tsdata->vcc)) {
453		error = PTR_ERR(tsdata->vcc);
454		if (error != -EPROBE_DEFER)
455			dev_err(&client->dev,
456				"failed to request regulator: %d\n", error);
457		return error;
458	}
459
460	error = regulator_enable(tsdata->vcc);
461	if (error < 0) {
462		dev_err(&client->dev, "failed to enable vcc: %d\n", error);
463		return error;
464	}
465
466	error = devm_add_action_or_reset(&client->dev,
467					 hycon_hy46xx_disable_regulator,
468					 tsdata);
469	if (error)
470		return error;
471
472	tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
473						     "reset", GPIOD_OUT_LOW);
474	if (IS_ERR(tsdata->reset_gpio)) {
475		error = PTR_ERR(tsdata->reset_gpio);
476		dev_err(&client->dev,
477			"Failed to request GPIO reset pin, error %d\n", error);
478		return error;
479	}
480
481	if (tsdata->reset_gpio) {
482		usleep_range(5000, 6000);
483		gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
484		usleep_range(5000, 6000);
485		gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
486		msleep(1000);
487	}
488
489	input = devm_input_allocate_device(&client->dev);
490	if (!input) {
491		dev_err(&client->dev, "failed to allocate input device.\n");
492		return -ENOMEM;
493	}
494
495	mutex_init(&tsdata->mutex);
496	tsdata->client = client;
497	tsdata->input = input;
498
499	tsdata->regmap = devm_regmap_init_i2c(client,
500					      &hycon_hy46xx_i2c_regmap_config);
501	if (IS_ERR(tsdata->regmap)) {
502		dev_err(&client->dev, "regmap allocation failed\n");
503		return PTR_ERR(tsdata->regmap);
504	}
505
506	hycon_hy46xx_get_defaults(&client->dev, tsdata);
507	hycon_hy46xx_get_parameters(tsdata);
508
509	input->name = "Hycon Capacitive Touch";
510	input->id.bustype = BUS_I2C;
511	input->dev.parent = &client->dev;
512
513	input_set_abs_params(input, ABS_MT_POSITION_X, 0, -1, 0, 0);
514	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, -1, 0, 0);
515
516	touchscreen_parse_properties(input, true, &tsdata->prop);
517
518	error = input_mt_init_slots(input, HY46XX_MAX_SUPPORTED_POINTS,
519				    INPUT_MT_DIRECT);
520	if (error) {
521		dev_err(&client->dev, "Unable to init MT slots.\n");
522		return error;
523	}
524
525	i2c_set_clientdata(client, tsdata);
526
527	error = devm_request_threaded_irq(&client->dev, client->irq,
528					  NULL, hycon_hy46xx_isr, IRQF_ONESHOT,
529					  client->name, tsdata);
530	if (error) {
531		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
532		return error;
533	}
534
535	error = input_register_device(input);
536	if (error)
537		return error;
538
539	dev_dbg(&client->dev,
540		"HYCON HY46XX initialized: IRQ %d, Reset pin %d.\n",
541		client->irq,
542		tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
543
544	return 0;
545}
546
547static const struct i2c_device_id hycon_hy46xx_id[] = {
548	{ .name = "hy4613" },
549	{ .name = "hy4614" },
550	{ .name = "hy4621" },
551	{ .name = "hy4623" },
552	{ .name = "hy4633" },
553	{ .name = "hy4635" },
554	{ /* sentinel */ }
555};
556MODULE_DEVICE_TABLE(i2c, hycon_hy46xx_id);
557
558static const struct of_device_id hycon_hy46xx_of_match[] = {
559	{ .compatible = "hycon,hy4613" },
560	{ .compatible = "hycon,hy4614" },
561	{ .compatible = "hycon,hy4621" },
562	{ .compatible = "hycon,hy4623" },
563	{ .compatible = "hycon,hy4633" },
564	{ .compatible = "hycon,hy4635" },
565	{ /* sentinel */ }
566};
567MODULE_DEVICE_TABLE(of, hycon_hy46xx_of_match);
568
569static struct i2c_driver hycon_hy46xx_driver = {
570	.driver = {
571		.name = "hycon_hy46xx",
572		.dev_groups = hycon_hy46xx_groups,
573		.of_match_table = hycon_hy46xx_of_match,
574		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
575	},
576	.id_table = hycon_hy46xx_id,
577	.probe = hycon_hy46xx_probe,
578};
579
580module_i2c_driver(hycon_hy46xx_driver);
581
582MODULE_AUTHOR("Giulio Benetti <giulio.benetti@benettiengineering.com>");
583MODULE_DESCRIPTION("HYCON HY46XX I2C Touchscreen Driver");
584MODULE_LICENSE("GPL v2");
585