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