[PATCH 7/9] Input: pixcir_i2c_ts: Implement Type B Multi Touch reporting

From: Roger Quadros
Date: Wed Dec 18 2013 - 04:24:46 EST


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 linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/