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