1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
283f66a6fSJelle van der Waa /*
383f66a6fSJelle van der Waa  * Copyright (C) 2016, Jelle van der Waa <jelle@vdwaa.nl>
483f66a6fSJelle van der Waa  */
583f66a6fSJelle van der Waa 
683f66a6fSJelle van der Waa #include <linux/delay.h>
783f66a6fSJelle van der Waa #include <linux/i2c.h>
883f66a6fSJelle van der Waa #include <linux/input.h>
983f66a6fSJelle van der Waa #include <linux/input/mt.h>
1083f66a6fSJelle van der Waa #include <linux/input/touchscreen.h>
1183f66a6fSJelle van der Waa #include <linux/interrupt.h>
1283f66a6fSJelle van der Waa #include <linux/module.h>
1383f66a6fSJelle van der Waa #include <linux/regulator/consumer.h>
1483f66a6fSJelle van der Waa #include <asm/unaligned.h>
1583f66a6fSJelle van der Waa 
1683f66a6fSJelle van der Waa #define ZET6223_MAX_FINGERS		16
1783f66a6fSJelle van der Waa #define ZET6223_MAX_PKT_SIZE		(3 + 4 * ZET6223_MAX_FINGERS)
1883f66a6fSJelle van der Waa 
1983f66a6fSJelle van der Waa #define ZET6223_CMD_INFO		0xB2
2083f66a6fSJelle van der Waa #define ZET6223_CMD_INFO_LENGTH		17
2183f66a6fSJelle van der Waa #define ZET6223_VALID_PACKET		0x3c
2283f66a6fSJelle van der Waa 
2383f66a6fSJelle van der Waa #define ZET6223_POWER_ON_DELAY_MSEC	30
2483f66a6fSJelle van der Waa 
2583f66a6fSJelle van der Waa struct zet6223_ts {
2683f66a6fSJelle van der Waa 	struct i2c_client *client;
2783f66a6fSJelle van der Waa 	struct input_dev *input;
2883f66a6fSJelle van der Waa 	struct regulator *vcc;
2983f66a6fSJelle van der Waa 	struct regulator *vio;
3083f66a6fSJelle van der Waa 	struct touchscreen_properties prop;
3183f66a6fSJelle van der Waa 	struct regulator_bulk_data supplies[2];
3283f66a6fSJelle van der Waa 	u16 max_x;
3383f66a6fSJelle van der Waa 	u16 max_y;
3483f66a6fSJelle van der Waa 	u8 fingernum;
3583f66a6fSJelle van der Waa };
3683f66a6fSJelle van der Waa 
zet6223_start(struct input_dev * dev)3783f66a6fSJelle van der Waa static int zet6223_start(struct input_dev *dev)
3883f66a6fSJelle van der Waa {
3983f66a6fSJelle van der Waa 	struct zet6223_ts *ts = input_get_drvdata(dev);
4083f66a6fSJelle van der Waa 
4183f66a6fSJelle van der Waa 	enable_irq(ts->client->irq);
4283f66a6fSJelle van der Waa 
4383f66a6fSJelle van der Waa 	return 0;
4483f66a6fSJelle van der Waa }
4583f66a6fSJelle van der Waa 
zet6223_stop(struct input_dev * dev)4683f66a6fSJelle van der Waa static void zet6223_stop(struct input_dev *dev)
4783f66a6fSJelle van der Waa {
4883f66a6fSJelle van der Waa 	struct zet6223_ts *ts = input_get_drvdata(dev);
4983f66a6fSJelle van der Waa 
5083f66a6fSJelle van der Waa 	disable_irq(ts->client->irq);
5183f66a6fSJelle van der Waa }
5283f66a6fSJelle van der Waa 
zet6223_irq(int irq,void * dev_id)5383f66a6fSJelle van der Waa static irqreturn_t zet6223_irq(int irq, void *dev_id)
5483f66a6fSJelle van der Waa {
5583f66a6fSJelle van der Waa 	struct zet6223_ts *ts = dev_id;
5683f66a6fSJelle van der Waa 	u16 finger_bits;
5783f66a6fSJelle van der Waa 
5883f66a6fSJelle van der Waa 	/*
5983f66a6fSJelle van der Waa 	 * First 3 bytes are an identifier, two bytes of finger data.
6083f66a6fSJelle van der Waa 	 * X, Y data per finger is 4 bytes.
6183f66a6fSJelle van der Waa 	 */
6283f66a6fSJelle van der Waa 	u8 bufsize = 3 + 4 * ts->fingernum;
6383f66a6fSJelle van der Waa 	u8 buf[ZET6223_MAX_PKT_SIZE];
6483f66a6fSJelle van der Waa 	int i;
6583f66a6fSJelle van der Waa 	int ret;
6683f66a6fSJelle van der Waa 	int error;
6783f66a6fSJelle van der Waa 
6883f66a6fSJelle van der Waa 	ret = i2c_master_recv(ts->client, buf, bufsize);
6983f66a6fSJelle van der Waa 	if (ret != bufsize) {
7083f66a6fSJelle van der Waa 		error = ret < 0 ? ret : -EIO;
7183f66a6fSJelle van der Waa 		dev_err_ratelimited(&ts->client->dev,
7283f66a6fSJelle van der Waa 				    "Error reading input data: %d\n", error);
7383f66a6fSJelle van der Waa 		return IRQ_HANDLED;
7483f66a6fSJelle van der Waa 	}
7583f66a6fSJelle van der Waa 
7683f66a6fSJelle van der Waa 	if (buf[0] != ZET6223_VALID_PACKET)
7783f66a6fSJelle van der Waa 		return IRQ_HANDLED;
7883f66a6fSJelle van der Waa 
7983f66a6fSJelle van der Waa 	finger_bits = get_unaligned_be16(buf + 1);
8083f66a6fSJelle van der Waa 	for (i = 0; i < ts->fingernum; i++) {
8183f66a6fSJelle van der Waa 		if (!(finger_bits & BIT(15 - i)))
8283f66a6fSJelle van der Waa 			continue;
8383f66a6fSJelle van der Waa 
8483f66a6fSJelle van der Waa 		input_mt_slot(ts->input, i);
8583f66a6fSJelle van der Waa 		input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
8683f66a6fSJelle van der Waa 		input_event(ts->input, EV_ABS, ABS_MT_POSITION_X,
8783f66a6fSJelle van der Waa 				((buf[i + 3] >> 4) << 8) + buf[i + 4]);
8883f66a6fSJelle van der Waa 		input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y,
8983f66a6fSJelle van der Waa 				((buf[i + 3] & 0xF) << 8) + buf[i + 5]);
9083f66a6fSJelle van der Waa 	}
9183f66a6fSJelle van der Waa 
9283f66a6fSJelle van der Waa 	input_mt_sync_frame(ts->input);
9383f66a6fSJelle van der Waa 	input_sync(ts->input);
9483f66a6fSJelle van der Waa 
9583f66a6fSJelle van der Waa 	return IRQ_HANDLED;
9683f66a6fSJelle van der Waa }
9783f66a6fSJelle van der Waa 
zet6223_power_off(void * _ts)9883f66a6fSJelle van der Waa static void zet6223_power_off(void *_ts)
9983f66a6fSJelle van der Waa {
10083f66a6fSJelle van der Waa 	struct zet6223_ts *ts = _ts;
10183f66a6fSJelle van der Waa 
10283f66a6fSJelle van der Waa 	regulator_bulk_disable(ARRAY_SIZE(ts->supplies), ts->supplies);
10383f66a6fSJelle van der Waa }
10483f66a6fSJelle van der Waa 
zet6223_power_on(struct zet6223_ts * ts)10583f66a6fSJelle van der Waa static int zet6223_power_on(struct zet6223_ts *ts)
10683f66a6fSJelle van der Waa {
10783f66a6fSJelle van der Waa 	struct device *dev = &ts->client->dev;
10883f66a6fSJelle van der Waa 	int error;
10983f66a6fSJelle van der Waa 
11083f66a6fSJelle van der Waa 	ts->supplies[0].supply = "vio";
11183f66a6fSJelle van der Waa 	ts->supplies[1].supply = "vcc";
11283f66a6fSJelle van der Waa 
11383f66a6fSJelle van der Waa 	error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->supplies),
11483f66a6fSJelle van der Waa 					ts->supplies);
11583f66a6fSJelle van der Waa 	if (error)
11683f66a6fSJelle van der Waa 		return error;
11783f66a6fSJelle van der Waa 
11883f66a6fSJelle van der Waa 	error = regulator_bulk_enable(ARRAY_SIZE(ts->supplies), ts->supplies);
11983f66a6fSJelle van der Waa 	if (error)
12083f66a6fSJelle van der Waa 		return error;
12183f66a6fSJelle van der Waa 
12283f66a6fSJelle van der Waa 	msleep(ZET6223_POWER_ON_DELAY_MSEC);
12383f66a6fSJelle van der Waa 
12483f66a6fSJelle van der Waa 	error = devm_add_action_or_reset(dev, zet6223_power_off, ts);
12583f66a6fSJelle van der Waa 	if (error) {
12683f66a6fSJelle van der Waa 		dev_err(dev, "failed to install poweroff action: %d\n", error);
12783f66a6fSJelle van der Waa 		return error;
12883f66a6fSJelle van der Waa 	}
12983f66a6fSJelle van der Waa 
13083f66a6fSJelle van der Waa 	return 0;
13183f66a6fSJelle van der Waa }
13283f66a6fSJelle van der Waa 
zet6223_query_device(struct zet6223_ts * ts)13383f66a6fSJelle van der Waa static int zet6223_query_device(struct zet6223_ts *ts)
13483f66a6fSJelle van der Waa {
13583f66a6fSJelle van der Waa 	u8 buf[ZET6223_CMD_INFO_LENGTH];
13683f66a6fSJelle van der Waa 	u8 cmd = ZET6223_CMD_INFO;
13783f66a6fSJelle van der Waa 	int ret;
13883f66a6fSJelle van der Waa 	int error;
13983f66a6fSJelle van der Waa 
14083f66a6fSJelle van der Waa 	ret = i2c_master_send(ts->client, &cmd, sizeof(cmd));
14183f66a6fSJelle van der Waa 	if (ret != sizeof(cmd)) {
14283f66a6fSJelle van der Waa 		error = ret < 0 ? ret : -EIO;
14383f66a6fSJelle van der Waa 		dev_err(&ts->client->dev,
14483f66a6fSJelle van der Waa 			"touchpanel info cmd failed: %d\n", error);
14583f66a6fSJelle van der Waa 		return error;
14683f66a6fSJelle van der Waa 	}
14783f66a6fSJelle van der Waa 
14883f66a6fSJelle van der Waa 	ret = i2c_master_recv(ts->client, buf, sizeof(buf));
14983f66a6fSJelle van der Waa 	if (ret != sizeof(buf)) {
15083f66a6fSJelle van der Waa 		error = ret < 0 ? ret : -EIO;
15183f66a6fSJelle van der Waa 		dev_err(&ts->client->dev,
15283f66a6fSJelle van der Waa 			"failed to retrieve touchpanel info: %d\n", error);
15383f66a6fSJelle van der Waa 		return error;
15483f66a6fSJelle van der Waa 	}
15583f66a6fSJelle van der Waa 
15683f66a6fSJelle van der Waa 	ts->fingernum = buf[15] & 0x7F;
15783f66a6fSJelle van der Waa 	if (ts->fingernum > ZET6223_MAX_FINGERS) {
15883f66a6fSJelle van der Waa 		dev_warn(&ts->client->dev,
15983f66a6fSJelle van der Waa 			 "touchpanel reports %d fingers, limiting to %d\n",
16083f66a6fSJelle van der Waa 			 ts->fingernum, ZET6223_MAX_FINGERS);
16183f66a6fSJelle van der Waa 		ts->fingernum = ZET6223_MAX_FINGERS;
16283f66a6fSJelle van der Waa 	}
16383f66a6fSJelle van der Waa 
16483f66a6fSJelle van der Waa 	ts->max_x = get_unaligned_le16(&buf[8]);
16583f66a6fSJelle van der Waa 	ts->max_y = get_unaligned_le16(&buf[10]);
16683f66a6fSJelle van der Waa 
16783f66a6fSJelle van der Waa 	return 0;
16883f66a6fSJelle van der Waa }
16983f66a6fSJelle van der Waa 
zet6223_probe(struct i2c_client * client)170f8684ea5SUwe Kleine-König static int zet6223_probe(struct i2c_client *client)
17183f66a6fSJelle van der Waa {
17283f66a6fSJelle van der Waa 	struct device *dev = &client->dev;
17383f66a6fSJelle van der Waa 	struct zet6223_ts *ts;
17483f66a6fSJelle van der Waa 	struct input_dev *input;
17583f66a6fSJelle van der Waa 	int error;
17683f66a6fSJelle van der Waa 
17783f66a6fSJelle van der Waa 	if (!client->irq) {
17883f66a6fSJelle van der Waa 		dev_err(dev, "no irq specified\n");
17983f66a6fSJelle van der Waa 		return -EINVAL;
18083f66a6fSJelle van der Waa 	}
18183f66a6fSJelle van der Waa 
18283f66a6fSJelle van der Waa 	ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
18383f66a6fSJelle van der Waa 	if (!ts)
18483f66a6fSJelle van der Waa 		return -ENOMEM;
18583f66a6fSJelle van der Waa 
18683f66a6fSJelle van der Waa 	ts->client = client;
18783f66a6fSJelle van der Waa 
18883f66a6fSJelle van der Waa 	error = zet6223_power_on(ts);
18983f66a6fSJelle van der Waa 	if (error)
19083f66a6fSJelle van der Waa 		return error;
19183f66a6fSJelle van der Waa 
19283f66a6fSJelle van der Waa 	error = zet6223_query_device(ts);
19383f66a6fSJelle van der Waa 	if (error)
19483f66a6fSJelle van der Waa 		return error;
19583f66a6fSJelle van der Waa 
19683f66a6fSJelle van der Waa 	ts->input = input = devm_input_allocate_device(dev);
19783f66a6fSJelle van der Waa 	if (!input)
19883f66a6fSJelle van der Waa 		return -ENOMEM;
19983f66a6fSJelle van der Waa 
20083f66a6fSJelle van der Waa 	input_set_drvdata(input, ts);
20183f66a6fSJelle van der Waa 
20283f66a6fSJelle van der Waa 	input->name = client->name;
20383f66a6fSJelle van der Waa 	input->id.bustype = BUS_I2C;
20483f66a6fSJelle van der Waa 	input->open = zet6223_start;
20583f66a6fSJelle van der Waa 	input->close = zet6223_stop;
20683f66a6fSJelle van der Waa 
20783f66a6fSJelle van der Waa 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0);
20883f66a6fSJelle van der Waa 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0);
20983f66a6fSJelle van der Waa 
21083f66a6fSJelle van der Waa 	touchscreen_parse_properties(input, true, &ts->prop);
21183f66a6fSJelle van der Waa 
21283f66a6fSJelle van der Waa 	error = input_mt_init_slots(input, ts->fingernum,
21383f66a6fSJelle van der Waa 				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
21483f66a6fSJelle van der Waa 	if (error)
21583f66a6fSJelle van der Waa 		return error;
21683f66a6fSJelle van der Waa 
21783f66a6fSJelle van der Waa 	error = devm_request_threaded_irq(dev, client->irq, NULL, zet6223_irq,
21883f66a6fSJelle van der Waa 					  IRQF_ONESHOT, client->name, ts);
21983f66a6fSJelle van der Waa 	if (error) {
22083f66a6fSJelle van der Waa 		dev_err(dev, "failed to request irq %d: %d\n",
22183f66a6fSJelle van der Waa 			client->irq, error);
22283f66a6fSJelle van der Waa 		return error;
22383f66a6fSJelle van der Waa 	}
22483f66a6fSJelle van der Waa 
22583f66a6fSJelle van der Waa 	zet6223_stop(input);
22683f66a6fSJelle van der Waa 
22783f66a6fSJelle van der Waa 	error = input_register_device(input);
22883f66a6fSJelle van der Waa 	if (error)
22983f66a6fSJelle van der Waa 		return error;
23083f66a6fSJelle van der Waa 
23183f66a6fSJelle van der Waa 	return 0;
23283f66a6fSJelle van der Waa }
23383f66a6fSJelle van der Waa 
23483f66a6fSJelle van der Waa static const struct of_device_id zet6223_of_match[] = {
23583f66a6fSJelle van der Waa 	{ .compatible = "zeitec,zet6223" },
23683f66a6fSJelle van der Waa 	{ }
23783f66a6fSJelle van der Waa };
238a1b53592SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, zet6223_of_match);
23983f66a6fSJelle van der Waa 
24083f66a6fSJelle van der Waa static const struct i2c_device_id zet6223_id[] = {
24183f66a6fSJelle van der Waa 	{ "zet6223", 0},
24283f66a6fSJelle van der Waa 	{ }
24383f66a6fSJelle van der Waa };
24483f66a6fSJelle van der Waa MODULE_DEVICE_TABLE(i2c, zet6223_id);
24583f66a6fSJelle van der Waa 
24683f66a6fSJelle van der Waa static struct i2c_driver zet6223_driver = {
24783f66a6fSJelle van der Waa 	.driver = {
24883f66a6fSJelle van der Waa 		.name = "zet6223",
24983f66a6fSJelle van der Waa 		.of_match_table = zet6223_of_match,
25083f66a6fSJelle van der Waa 	},
251*d8bde56dSUwe Kleine-König 	.probe = zet6223_probe,
25283f66a6fSJelle van der Waa 	.id_table = zet6223_id
25383f66a6fSJelle van der Waa };
25483f66a6fSJelle van der Waa module_i2c_driver(zet6223_driver);
25583f66a6fSJelle van der Waa 
25683f66a6fSJelle van der Waa MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>");
25783f66a6fSJelle van der Waa MODULE_DESCRIPTION("ZEITEC zet622x I2C touchscreen driver");
25883f66a6fSJelle van der Waa MODULE_LICENSE("GPL");
259