Linux Audio

Check our new training course

Buildroot integration, development and maintenance

Need a Buildroot system for your embedded project?
Loading...
v6.8
  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");
v6.2
  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 = scnprintf(buf, PAGE_SIZE, "%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};
277
278static const struct attribute_group hycon_hy46xx_attr_group = {
279	.attrs = hycon_hy46xx_attrs,
280};
281
282static void hycon_hy46xx_get_defaults(struct device *dev, struct hycon_hy46xx_data *tsdata)
283{
284	bool val_bool;
285	int error;
286	u32 val;
287
288	error = device_property_read_u32(dev, "hycon,threshold", &val);
289	if (!error) {
290		error = regmap_write(tsdata->regmap, HY46XX_THRESHOLD, val);
291		if (error < 0)
292			goto out;
293
294		tsdata->threshold = val;
295	}
296
297	val_bool = device_property_read_bool(dev, "hycon,glove-enable");
298	error = regmap_write(tsdata->regmap, HY46XX_GLOVE_EN, val_bool);
299	if (error < 0)
300		goto out;
301	tsdata->glove_enable = val_bool;
302
303	error = device_property_read_u32(dev, "hycon,report-speed-hz", &val);
304	if (!error) {
305		error = regmap_write(tsdata->regmap, HY46XX_REPORT_SPEED, val);
306		if (error < 0)
307			goto out;
308
309		tsdata->report_speed = val;
310	}
311
312	val_bool = device_property_read_bool(dev, "hycon,noise-filter-enable");
313	error = regmap_write(tsdata->regmap, HY46XX_PWR_NOISE_EN, val_bool);
314	if (error < 0)
315		goto out;
316	tsdata->noise_filter_enable = val_bool;
317
318	error = device_property_read_u32(dev, "hycon,filter-data", &val);
319	if (!error) {
320		error = regmap_write(tsdata->regmap, HY46XX_FILTER_DATA, val);
321		if (error < 0)
322			goto out;
323
324		tsdata->filter_data = val;
325	}
326
327	error = device_property_read_u32(dev, "hycon,gain", &val);
328	if (!error) {
329		error = regmap_write(tsdata->regmap, HY46XX_GAIN, val);
330		if (error < 0)
331			goto out;
332
333		tsdata->gain = val;
334	}
335
336	error = device_property_read_u32(dev, "hycon,edge-offset", &val);
337	if (!error) {
338		error = regmap_write(tsdata->regmap, HY46XX_EDGE_OFFSET, val);
339		if (error < 0)
340			goto out;
341
342		tsdata->edge_offset = val;
343	}
344
345	return;
346out:
347	dev_err(&tsdata->client->dev, "Failed to set default settings");
348}
349
350static void hycon_hy46xx_get_parameters(struct hycon_hy46xx_data *tsdata)
351{
352	int error;
353	u32 val;
354
355	error = regmap_read(tsdata->regmap, HY46XX_THRESHOLD, &val);
356	if (error < 0)
357		goto out;
358	tsdata->threshold = val;
359
360	error = regmap_read(tsdata->regmap, HY46XX_GLOVE_EN, &val);
361	if (error < 0)
362		goto out;
363	tsdata->glove_enable = val;
364
365	error = regmap_read(tsdata->regmap, HY46XX_REPORT_SPEED, &val);
366	if (error < 0)
367		goto out;
368	tsdata->report_speed = val;
369
370	error = regmap_read(tsdata->regmap, HY46XX_PWR_NOISE_EN, &val);
371	if (error < 0)
372		goto out;
373	tsdata->noise_filter_enable = val;
374
375	error = regmap_read(tsdata->regmap, HY46XX_FILTER_DATA, &val);
376	if (error < 0)
377		goto out;
378	tsdata->filter_data = val;
379
380	error = regmap_read(tsdata->regmap, HY46XX_GAIN, &val);
381	if (error < 0)
382		goto out;
383	tsdata->gain = val;
384
385	error = regmap_read(tsdata->regmap, HY46XX_EDGE_OFFSET, &val);
386	if (error < 0)
387		goto out;
388	tsdata->edge_offset = val;
389
390	error = regmap_read(tsdata->regmap, HY46XX_RX_NR_USED, &val);
391	if (error < 0)
392		goto out;
393	tsdata->rx_number_used = val;
394
395	error = regmap_read(tsdata->regmap, HY46XX_TX_NR_USED, &val);
396	if (error < 0)
397		goto out;
398	tsdata->tx_number_used = val;
399
400	error = regmap_read(tsdata->regmap, HY46XX_PWR_MODE, &val);
401	if (error < 0)
402		goto out;
403	tsdata->power_mode = val;
404
405	error = regmap_read(tsdata->regmap, HY46XX_FW_VERSION, &val);
406	if (error < 0)
407		goto out;
408	tsdata->fw_version = val;
409
410	error = regmap_read(tsdata->regmap, HY46XX_LIB_VERSION, &val);
411	if (error < 0)
412		goto out;
413	tsdata->lib_version = val;
414
415	error = regmap_read(tsdata->regmap, HY46XX_TP_INFO, &val);
416	if (error < 0)
417		goto out;
418	tsdata->tp_information = val;
419
420	error = regmap_read(tsdata->regmap, HY46XX_TP_CHIP_ID, &val);
421	if (error < 0)
422		goto out;
423	tsdata->tp_chip_id = val;
424
425	error = regmap_read(tsdata->regmap, HY46XX_BOOT_VER, &val);
426	if (error < 0)
427		goto out;
428	tsdata->bootloader_version = val;
429
430	return;
431out:
432	dev_err(&tsdata->client->dev, "Failed to read default settings");
433}
434
435static void hycon_hy46xx_disable_regulator(void *arg)
436{
437	struct hycon_hy46xx_data *data = arg;
438
439	regulator_disable(data->vcc);
440}
441
442static int hycon_hy46xx_probe(struct i2c_client *client)
443{
444	struct hycon_hy46xx_data *tsdata;
445	struct input_dev *input;
446	int error;
447
448	dev_dbg(&client->dev, "probing for HYCON HY46XX I2C\n");
449
450	tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
451	if (!tsdata)
452		return -ENOMEM;
453
454	tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
455	if (IS_ERR(tsdata->vcc)) {
456		error = PTR_ERR(tsdata->vcc);
457		if (error != -EPROBE_DEFER)
458			dev_err(&client->dev,
459				"failed to request regulator: %d\n", error);
460		return error;
461	}
462
463	error = regulator_enable(tsdata->vcc);
464	if (error < 0) {
465		dev_err(&client->dev, "failed to enable vcc: %d\n", error);
466		return error;
467	}
468
469	error = devm_add_action_or_reset(&client->dev,
470					 hycon_hy46xx_disable_regulator,
471					 tsdata);
472	if (error)
473		return error;
474
475	tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
476						     "reset", GPIOD_OUT_LOW);
477	if (IS_ERR(tsdata->reset_gpio)) {
478		error = PTR_ERR(tsdata->reset_gpio);
479		dev_err(&client->dev,
480			"Failed to request GPIO reset pin, error %d\n", error);
481		return error;
482	}
483
484	if (tsdata->reset_gpio) {
485		usleep_range(5000, 6000);
486		gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
487		usleep_range(5000, 6000);
488		gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
489		msleep(1000);
490	}
491
492	input = devm_input_allocate_device(&client->dev);
493	if (!input) {
494		dev_err(&client->dev, "failed to allocate input device.\n");
495		return -ENOMEM;
496	}
497
498	mutex_init(&tsdata->mutex);
499	tsdata->client = client;
500	tsdata->input = input;
501
502	tsdata->regmap = devm_regmap_init_i2c(client,
503					      &hycon_hy46xx_i2c_regmap_config);
504	if (IS_ERR(tsdata->regmap)) {
505		dev_err(&client->dev, "regmap allocation failed\n");
506		return PTR_ERR(tsdata->regmap);
507	}
508
509	hycon_hy46xx_get_defaults(&client->dev, tsdata);
510	hycon_hy46xx_get_parameters(tsdata);
511
512	input->name = "Hycon Capacitive Touch";
513	input->id.bustype = BUS_I2C;
514	input->dev.parent = &client->dev;
515
516	input_set_abs_params(input, ABS_MT_POSITION_X, 0, -1, 0, 0);
517	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, -1, 0, 0);
518
519	touchscreen_parse_properties(input, true, &tsdata->prop);
520
521	error = input_mt_init_slots(input, HY46XX_MAX_SUPPORTED_POINTS,
522				    INPUT_MT_DIRECT);
523	if (error) {
524		dev_err(&client->dev, "Unable to init MT slots.\n");
525		return error;
526	}
527
528	i2c_set_clientdata(client, tsdata);
529
530	error = devm_request_threaded_irq(&client->dev, client->irq,
531					  NULL, hycon_hy46xx_isr, IRQF_ONESHOT,
532					  client->name, tsdata);
533	if (error) {
534		dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
535		return error;
536	}
537
538	error = devm_device_add_group(&client->dev, &hycon_hy46xx_attr_group);
539	if (error)
540		return error;
541
542	error = input_register_device(input);
543	if (error)
544		return error;
545
546	dev_dbg(&client->dev,
547		"HYCON HY46XX initialized: IRQ %d, Reset pin %d.\n",
548		client->irq,
549		tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
550
551	return 0;
552}
553
554static const struct i2c_device_id hycon_hy46xx_id[] = {
555	{ .name = "hy4613" },
556	{ .name = "hy4614" },
557	{ .name = "hy4621" },
558	{ .name = "hy4623" },
559	{ .name = "hy4633" },
560	{ .name = "hy4635" },
561	{ /* sentinel */ }
562};
563MODULE_DEVICE_TABLE(i2c, hycon_hy46xx_id);
564
565static const struct of_device_id hycon_hy46xx_of_match[] = {
566	{ .compatible = "hycon,hy4613" },
567	{ .compatible = "hycon,hy4614" },
568	{ .compatible = "hycon,hy4621" },
569	{ .compatible = "hycon,hy4623" },
570	{ .compatible = "hycon,hy4633" },
571	{ .compatible = "hycon,hy4635" },
572	{ /* sentinel */ }
573};
574MODULE_DEVICE_TABLE(of, hycon_hy46xx_of_match);
575
576static struct i2c_driver hycon_hy46xx_driver = {
577	.driver = {
578		.name = "hycon_hy46xx",
 
579		.of_match_table = hycon_hy46xx_of_match,
580		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
581	},
582	.id_table = hycon_hy46xx_id,
583	.probe_new = hycon_hy46xx_probe,
584};
585
586module_i2c_driver(hycon_hy46xx_driver);
587
588MODULE_AUTHOR("Giulio Benetti <giulio.benetti@benettiengineering.com>");
589MODULE_DESCRIPTION("HYCON HY46XX I2C Touchscreen Driver");
590MODULE_LICENSE("GPL v2");