Some pixcir controllers e.g. tangoC family report finger IDs with the co-ordinates and are more suitable for Type-B MT protocol. Signed-off-by: Roger Quadros <rogerq@xxxxxx> Acked-by: Mugunthan V N <mugunthanvnm@xxxxxx> --- drivers/input/touchscreen/pixcir_i2c_ts.c | 202 +++++++++++++++++++++++------- 1 file changed, 155 insertions(+), 47 deletions(-) diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index ff68246..9e14415 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -23,84 +23,173 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/input/pixcir_ts.h> #include <linux/gpio.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_device.h> +#define MAX_FINGERS 5 /* Maximum supported by the driver */ + struct pixcir_i2c_ts_data { struct i2c_client *client; struct input_dev *input; const struct pixcir_ts_platform_data *pdata; bool exiting; + u8 max_fingers; /* Maximum supported by the chip */ }; -static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) +static void pixcir_ts_typea_report(struct pixcir_i2c_ts_data *tsdata) { - struct pixcir_i2c_ts_data *tsdata = data; + const struct pixcir_ts_platform_data *pdata = tsdata->pdata; u8 rdbuf[10], wrbuf[1] = { 0 }; u8 touch; int ret; - ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf)); - if (ret != sizeof(wrbuf)) { - dev_err(&tsdata->client->dev, - "%s: i2c_master_send failed(), ret=%d\n", - __func__, ret); - return; - } + while (!tsdata->exiting) { - ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf)); - if (ret != sizeof(rdbuf)) { - dev_err(&tsdata->client->dev, - "%s: i2c_master_recv failed(), ret=%d\n", - __func__, ret); - return; - } + ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf)); + if (ret != sizeof(wrbuf)) { + dev_err(&tsdata->client->dev, + "%s: i2c_master_send failed(), ret=%d\n", + __func__, ret); + return; + } + + ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf)); + if (ret != sizeof(rdbuf)) { + dev_err(&tsdata->client->dev, + "%s: i2c_master_recv failed(), ret=%d\n", + __func__, ret); + return; + } - touch = rdbuf[0]; - if (touch) { - u16 posx1 = (rdbuf[3] << 8) | rdbuf[2]; - u16 posy1 = (rdbuf[5] << 8) | rdbuf[4]; - u16 posx2 = (rdbuf[7] << 8) | rdbuf[6]; - u16 posy2 = (rdbuf[9] << 8) | rdbuf[8]; - - input_report_key(tsdata->input, BTN_TOUCH, 1); - input_report_abs(tsdata->input, ABS_X, posx1); - input_report_abs(tsdata->input, ABS_Y, posy1); - - input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1); - input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1); - input_mt_sync(tsdata->input); - - if (touch == 2) { - input_report_abs(tsdata->input, - ABS_MT_POSITION_X, posx2); - input_report_abs(tsdata->input, - ABS_MT_POSITION_Y, posy2); + touch = rdbuf[0]; + if (touch) { + u16 posx1 = (rdbuf[3] << 8) | rdbuf[2]; + u16 posy1 = (rdbuf[5] << 8) | rdbuf[4]; + u16 posx2 = (rdbuf[7] << 8) | rdbuf[6]; + u16 posy2 = (rdbuf[9] << 8) | rdbuf[8]; + + input_report_key(tsdata->input, BTN_TOUCH, 1); + input_report_abs(tsdata->input, ABS_X, posx1); + input_report_abs(tsdata->input, ABS_Y, posy1); + + input_report_abs(tsdata->input, ABS_MT_POSITION_X, + posx1); + input_report_abs(tsdata->input, ABS_MT_POSITION_Y, + posy1); input_mt_sync(tsdata->input); + + if (touch == 2) { + input_report_abs(tsdata->input, + ABS_MT_POSITION_X, posx2); + input_report_abs(tsdata->input, + ABS_MT_POSITION_Y, posy2); + input_mt_sync(tsdata->input); + } + } else { + input_report_key(tsdata->input, BTN_TOUCH, 0); } - } else { - input_report_key(tsdata->input, BTN_TOUCH, 0); - } - input_sync(tsdata->input); + input_sync(tsdata->input); + + if (gpio_get_value(pdata->gpio_attb)) + break; + + msleep(20); + } } -static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) +static void pixcir_ts_typeb_report(struct pixcir_i2c_ts_data *ts) { - struct pixcir_i2c_ts_data *tsdata = dev_id; - const struct pixcir_ts_platform_data *pdata = tsdata->pdata; + const struct pixcir_ts_platform_data *pdata = ts->pdata; + struct device *dev = &ts->client->dev; + u8 rdbuf[32], wrbuf[1] = { 0 }; + u8 *bufptr; + u8 num_fingers; + u8 unreliable; + int ret, i; + + while (!ts->exiting) { + + ret = i2c_master_send(ts->client, wrbuf, sizeof(wrbuf)); + if (ret != sizeof(wrbuf)) { + dev_err(dev, "%s: i2c_master_send failed(), ret=%d\n", + __func__, ret); + return; + } - while (!tsdata->exiting) { - pixcir_ts_poscheck(tsdata); + ret = i2c_master_recv(ts->client, rdbuf, sizeof(rdbuf)); + if (ret != sizeof(rdbuf)) { + dev_err(dev, "%s: i2c_master_recv failed(), ret=%d\n", + __func__, ret); + return; + } + + unreliable = rdbuf[0] & 0xe0; + + if (unreliable) + goto next; /* ignore unreliable data */ + + num_fingers = rdbuf[0] & 0x7; + bufptr = &rdbuf[2]; + if (num_fingers > ts->max_fingers) { + num_fingers = ts->max_fingers; + dev_dbg(dev, "limiting num_fingers to %d\n", + num_fingers); + } + + for (i = 0; i < num_fingers; i++) { + u8 id; + unsigned int x, y; + int slot; + + id = bufptr[4]; + slot = input_mt_get_slot_by_key(ts->input, id); + if (slot < 0) { + dev_dbg(dev, "no free slot for id 0x%x\n", id); + continue; + } + + + x = bufptr[1] << 8 | bufptr[0]; + y = bufptr[3] << 8 | bufptr[2]; + + input_mt_slot(ts->input, slot); + input_mt_report_slot_state(ts->input, + MT_TOOL_FINGER, true); + + input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, x); + input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, y); + + bufptr = &bufptr[5]; + dev_dbg(dev, "%d: id 0x%x slot %d, x %d, y %d\n", + i, id, slot, x, y); + } + + /* One frame is complete so sync it */ + input_mt_sync_frame(ts->input); + input_sync(ts->input); + +next: if (gpio_get_value(pdata->gpio_attb)) break; - msleep(20); + usleep_range(2000, 5000); } +} + +static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) +{ + struct pixcir_i2c_ts_data *tsdata = dev_id; + + if (tsdata->input->mt) + pixcir_ts_typeb_report(tsdata); + else + pixcir_ts_typea_report(tsdata); return IRQ_HANDLED; } @@ -376,9 +465,9 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input->open = pixcir_input_open; input->close = pixcir_input_close; - __set_bit(EV_KEY, input->evbit); __set_bit(EV_ABS, input->evbit); __set_bit(BTN_TOUCH, input->keybit); + input_set_abs_params(input, ABS_X, 0, pdata->x_size - 1, 0, 0); input_set_abs_params(input, ABS_Y, @@ -388,6 +477,25 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_size - 1, 0, 0); + /* Type-B Multi-Touch support */ + if (pdata->chip.num_report_ids) { + const struct pixcir_i2c_chip_data *chip = &pdata->chip; + + tsdata->max_fingers = chip->num_report_ids; + if (tsdata->max_fingers > MAX_FINGERS) { + dev_info(dev, "Limiting maximum fingers to %d\n", + MAX_FINGERS); + tsdata->max_fingers = MAX_FINGERS; + } + + error = input_mt_init_slots(input, tsdata->max_fingers, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(dev, "Error initializing Multi-Touch slots\n"); + return error; + } + } + input_set_drvdata(input, tsdata); error = devm_gpio_request_one(dev, pdata->gpio_attb, -- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html