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 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 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 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 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 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 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 170*f8684ea5SUwe 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*f8684ea5SUwe Kleine-König .probe_new = 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