@@ -41,6 +41,7 @@
#define REPORT_MODE_VENDOR 0x3
/* Multiple Touch Mode */
#define REPORT_MODE_MTTOUCH 0x4
+#define REPORT_MODE_MTTOUCH_NEW 0x6
#define MAX_SUPPORT_POINTS 5
@@ -51,8 +52,9 @@
#define EVENT_IN_RANGE (0x1 << 1)
#define EVENT_DOWN_UP (0X1 << 0)
-#define MAX_I2C_DATA_LEN 10
+#define MAX_I2C_DATA_LEN (64 + 2)
+#define EGALAX_POINT_SIZE 10
#define EGALAX_MAX_TRIES 100
struct egalax_ts {
@@ -62,41 +64,66 @@ struct egalax_ts {
struct touchscreen_properties props;
};
-static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
+static int egalax_ts_handle_points(struct egalax_ts *ts, u8 *msg,
+ int num_points)
{
- struct egalax_ts *ts = dev_id;
struct input_dev *input_dev = ts->input_dev;
struct i2c_client *client = ts->client;
- u8 buf[MAX_I2C_DATA_LEN];
- int id, ret, x, y, z;
- int tries = 0;
- bool down, valid;
- u8 state;
+ bool down;
+ int i, id, x, y;
+
+ for (i = 0; i < min(MAX_SUPPORT_POINTS, num_points);
+ i++, msg += EGALAX_POINT_SIZE, num_points--) {
+ down = !!(msg[0] & EVENT_DOWN_UP);
+ id = msg[1];
+ x = (msg[3] << 8) | msg[2];
+ y = (msg[5] << 8) | msg[4];
+
+ if (id > 10) {
+ dev_warn(&client->dev,
+ "dropping out of range event %d\n", id);
+ continue;
+ }
+
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
+
+ dev_dbg(&client->dev, "%s id:%d x:%d y:%d",
+ down ? "down" : "up", id, x, y);
+
+ if (down)
+ touchscreen_report_pos(input_dev, &ts->props,
+ x, y, true);
+ }
- do {
- ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
- } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
+ if (!num_points) {
+ input_mt_sync_frame(input_dev);
+ input_sync(input_dev);
+ }
- if (ret < 0)
- return IRQ_HANDLED;
+ return num_points;
+}
- if (buf[0] != REPORT_MODE_MTTOUCH) {
- /* ignore mouse events and vendor events */
- return IRQ_HANDLED;
- }
+static void egalax_ts_handle_points_legacy(struct egalax_ts *ts, u8 *msg)
+{
+ struct input_dev *input_dev = ts->input_dev;
+ struct i2c_client *client = ts->client;
+ bool down, valid;
+ int id, x, y, z;
+ u8 state;
- state = buf[1];
- x = (buf[3] << 8) | buf[2];
- y = (buf[5] << 8) | buf[4];
- z = (buf[7] << 8) | buf[6];
+ state = msg[0];
+ x = (msg[2] << 8) | msg[2];
+ y = (msg[4] << 8) | msg[3];
+ z = (msg[6] << 8) | msg[5];
valid = state & EVENT_VALID_MASK;
id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
down = state & EVENT_DOWN_UP;
- if (!valid || id > MAX_SUPPORT_POINTS) {
+ if (!valid || id > 5) {
dev_dbg(&client->dev, "point invalid\n");
- return IRQ_HANDLED;
+ return;
}
input_mt_slot(input_dev, id);
@@ -112,6 +139,52 @@ static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
input_mt_report_pointer_emulation(input_dev, true);
input_sync(input_dev);
+}
+
+static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
+{
+ struct egalax_ts *ts = dev_id;
+ struct i2c_client *client = ts->client;
+ u8 buf[MAX_I2C_DATA_LEN], *payload;
+ int ret, num_points, tries = 0;
+ int remaining = 0;
+
+again:
+ do {
+ ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
+ } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
+
+ if (ret < 0) {
+ dev_err_ratelimited(&client->dev,
+ "reading touch info failed: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ if (ts->protocol_version == 0) {
+ payload = &buf[0];
+ num_points = 1;
+ } else {
+ payload = &buf[2];
+ num_points = buf[3];
+
+ }
+
+ switch (payload[0]) {
+ case REPORT_MODE_MTTOUCH:
+ egalax_ts_handle_points_legacy(ts, payload + 1);
+ break;
+ case REPORT_MODE_MTTOUCH_NEW:
+ if (!remaining)
+ remaining = payload[1];
+ remaining = egalax_ts_handle_points(ts, payload + 2, remaining);
+ break;
+ default:
+ /* ignore unknown report IDs */
+ break;
+ }
+
+ if (remaining)
+ goto again;
return IRQ_HANDLED;
}
@@ -166,6 +239,7 @@ static int egalax_ts_probe(struct i2c_client *client,
{
struct egalax_ts *ts;
struct input_dev *input_dev;
+ unsigned int max_points = MAX_SUPPORT_POINTS;
int error;
ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL);
@@ -203,14 +277,20 @@ static int egalax_ts_probe(struct i2c_client *client,
input_dev->id.bustype = BUS_I2C;
touchscreen_parse_properties(ts->input_dev, true, &ts->props);
- if (!ts->props.max_x && !ts->props.max_y)
- ts->props.max_x = ts->props.max_y = 32760;
+ if (!ts->props.max_x && !ts->props.max_y) {
+ if (ts->protocol_version == 0)
+ ts->props.max_x = ts->props.max_y = 32760;
+ else
+ ts->props.max_x = ts->props.max_y = 4095;
+ }
+ if (ts->protocol_version != 0)
+ max_points *= 2;
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, ts->props.max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, ts->props.max_y, 0, 0);
- input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS,
+ input_mt_init_slots(input_dev, max_points,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
This implements support for the new protocol that supports up to 10 touch slots spread over up to 2 i2c messages. Signed-off-by: Lucas Stach <l.stach@pengutronix.de> --- drivers/input/touchscreen/egalax_ts.c | 132 +++++++++++++++++++++----- 1 file changed, 106 insertions(+), 26 deletions(-)