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 <rog...@ti.com>
Acked-by: Mugunthan V N <mugunthan...@ti.com>
---
 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 linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to