Message ID | 2b5e15ea600c33dfab4aa50e360ec553f1af7db0.1576079249.git.mirq-linux@rere.qmqm.pl (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | input: elants: Support Asus TF300T touchscreen | expand |
11.12.2019 19:03, Michał Mirosław пишет: > Support ELAN touchpad sensor with older firmware as found on eg. Asus > Transformer Pads. > > Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl> > --- > drivers/input/touchscreen/elants_i2c.c | 36 ++++++++++++++++++-------- > 1 file changed, 25 insertions(+), 11 deletions(-) > > diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c > index 887888c53996..eadd26d5a06f 100644 > --- a/drivers/input/touchscreen/elants_i2c.c > +++ b/drivers/input/touchscreen/elants_i2c.c > @@ -65,6 +65,7 @@ > #define CMD_HEADER_REK 0x66 > > /* FW position data */ > +#define PACKET_SIZE_OLD 40 > #define PACKET_SIZE 55 > #define MAX_CONTACT_NUM 10 > #define FW_POS_HEADER 0 > @@ -792,7 +793,8 @@ static int elants_i2c_fw_update(struct elants_data *ts) > * Event reporting. > */ > > -static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) > +static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf, > + size_t report_len) > { > struct input_dev *input = ts->input; > unsigned int n_fingers; > @@ -804,7 +806,8 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) > buf[FW_POS_STATE]; > > dev_dbg(&ts->client->dev, > - "n_fingers: %u, state: %04x\n", n_fingers, finger_state); > + "n_fingers: %u, state: %04x, report_len: %zu\n", > + n_fingers, finger_state, report_len); > > for (i = 0; i < MAX_CONTACT_NUM && n_fingers; i++) { > if (finger_state & 1) { > @@ -814,8 +817,16 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) > pos = &buf[FW_POS_XY + i * 3]; > x = (((u16)pos[0] & 0xf0) << 4) | pos[1]; > y = (((u16)pos[0] & 0x0f) << 8) | pos[2]; > - p = buf[FW_POS_PRESSURE + i]; > - w = buf[FW_POS_WIDTH + i]; > + if (report_len == PACKET_SIZE_OLD) { > + w = buf[FW_POS_WIDTH + i / 2]; > + w >>= 4 * (~i & 1); // little-endian-nibbles > + w |= w << 4; > + w |= !w; > + p = w; Did you copy this from the downstream driver as-is? I'm looking at the Nexus 7 driver and it does the following for older format: u8 size_idx[] = { 35, 35, 36, 36, 37, 37, 38, 38, 39, 39 }; unsigned int s; if (i & 1) s = buf[size_idx[i]]; else s = buf[size_idx[i]] / 16; w = s & 0xf; p = s * 16; > + } else { > + p = buf[FW_POS_PRESSURE + i]; > + w = buf[FW_POS_WIDTH + i]; > + } > > dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n", > i, x, y, p, w); > @@ -848,7 +859,8 @@ static u8 elants_i2c_calculate_checksum(u8 *buf) > return checksum; > } > > -static void elants_i2c_event(struct elants_data *ts, u8 *buf) > +static void elants_i2c_event(struct elants_data *ts, u8 *buf, > + size_t report_len) > { > u8 checksum = elants_i2c_calculate_checksum(buf); > > @@ -862,7 +874,7 @@ static void elants_i2c_event(struct elants_data *ts, u8 *buf) > "%s: unknown packet type: %02x\n", > __func__, buf[FW_POS_HEADER]); > else > - elants_i2c_mt_event(ts, buf); > + elants_i2c_mt_event(ts, buf, report_len); > } > > static irqreturn_t elants_i2c_irq(int irq, void *_dev) > @@ -920,7 +932,8 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) > break; > > case QUEUE_HEADER_SINGLE: > - elants_i2c_event(ts, &ts->buf[HEADER_SIZE]); > + elants_i2c_event(ts, &ts->buf[HEADER_SIZE], > + ts->buf[FW_HDR_LENGTH]); > break; > > case QUEUE_HEADER_NORMAL: > @@ -933,17 +946,18 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) > } > > report_len = ts->buf[FW_HDR_LENGTH] / report_count; > - if (report_len != PACKET_SIZE) { > + if (report_len != PACKET_SIZE && > + report_len != PACKET_SIZE_OLD) { > dev_err(&client->dev, > - "mismatching report length: %*ph\n", > + "unsupported report length: %*ph\n", > HEADER_SIZE, ts->buf); > break; > } > > for (i = 0; i < report_count; i++) { > u8 *buf = ts->buf + HEADER_SIZE + > - i * PACKET_SIZE; > - elants_i2c_event(ts, buf); > + i * report_len; > + elants_i2c_event(ts, buf, report_len); > } > break; > >
On Thu, Dec 12, 2019 at 03:54:13AM +0300, Dmitry Osipenko wrote: > 11.12.2019 19:03, Michał Mirosław пишет: > > Support ELAN touchpad sensor with older firmware as found on eg. Asus > > Transformer Pads. [...] > > @@ -814,8 +817,16 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) > > pos = &buf[FW_POS_XY + i * 3]; > > x = (((u16)pos[0] & 0xf0) << 4) | pos[1]; > > y = (((u16)pos[0] & 0x0f) << 8) | pos[2]; > > - p = buf[FW_POS_PRESSURE + i]; > > - w = buf[FW_POS_WIDTH + i]; > > + if (report_len == PACKET_SIZE_OLD) { > > + w = buf[FW_POS_WIDTH + i / 2]; > > + w >>= 4 * (~i & 1); // little-endian-nibbles > > + w |= w << 4; > > + w |= !w; > > + p = w; > > Did you copy this from the downstream driver as-is? I'm looking at the > Nexus 7 driver and it does the following for older format: > > u8 size_idx[] = { 35, 35, 36, 36, 37, 37, 38, 38, 39, 39 }; > unsigned int s; > > if (i & 1) > s = buf[size_idx[i]]; > else > s = buf[size_idx[i]] / 16; > > w = s & 0xf; > p = s * 16; This is the same thing modulo (w), which is scaled here to declared axis range (1-255 from 0-15, assuming 0 means "no touch" so it should not occur). OTOH, I admit, that I don't have any software that can verify those settings. It might be that eg. one of MT_PRESSURE or MT_TOUCH_MAJOR axes should be dropped in this case, but with no docs I can't be sure what the reported values really are. This is from the original (GPL) code dump labeled 'Asus 10_6_1_27_5': | touch_size = ((i & 0x01) ? buf[size_index[i]] : (buf[size_index[i]] >> 4)) & 0x0F; | if(touch_size == 0) touch_size = 1; | if (touch_size <= 7) | touch_size = touch_size << 5; | else | touch_size = 255; | | input_report_abs(idev, ABS_MT_TOUCH_MAJOR, touch_size); | input_report_abs(idev, ABS_MT_PRESSURE, touch_size); Best Regards, Michał Mirosław
12.12.2019 22:45, Michał Mirosław пишет: > On Thu, Dec 12, 2019 at 03:54:13AM +0300, Dmitry Osipenko wrote: >> 11.12.2019 19:03, Michał Mirosław пишет: >>> Support ELAN touchpad sensor with older firmware as found on eg. Asus >>> Transformer Pads. > [...] >>> @@ -814,8 +817,16 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) >>> pos = &buf[FW_POS_XY + i * 3]; >>> x = (((u16)pos[0] & 0xf0) << 4) | pos[1]; >>> y = (((u16)pos[0] & 0x0f) << 8) | pos[2]; >>> - p = buf[FW_POS_PRESSURE + i]; >>> - w = buf[FW_POS_WIDTH + i]; >>> + if (report_len == PACKET_SIZE_OLD) { >>> + w = buf[FW_POS_WIDTH + i / 2]; >>> + w >>= 4 * (~i & 1); // little-endian-nibbles >>> + w |= w << 4; >>> + w |= !w; >>> + p = w; >> >> Did you copy this from the downstream driver as-is? I'm looking at the >> Nexus 7 driver and it does the following for older format: >> >> u8 size_idx[] = { 35, 35, 36, 36, 37, 37, 38, 38, 39, 39 }; >> unsigned int s; >> >> if (i & 1) >> s = buf[size_idx[i]]; >> else >> s = buf[size_idx[i]] / 16; >> >> w = s & 0xf; >> p = s * 16; > > This is the same thing modulo (w), which is scaled here to declared axis > range (1-255 from 0-15, assuming 0 means "no touch" so it should not occur). > > OTOH, I admit, that I don't have any software that can verify those > settings. It might be that eg. one of MT_PRESSURE or MT_TOUCH_MAJOR axes > should be dropped in this case, but with no docs I can't be sure what > the reported values really are. > > This is from the original (GPL) code dump labeled 'Asus 10_6_1_27_5': > > | touch_size = ((i & 0x01) ? buf[size_index[i]] : (buf[size_index[i]] >> 4)) & 0x0F; > | if(touch_size == 0) touch_size = 1; > | if (touch_size <= 7) > | touch_size = touch_size << 5; > | else > | touch_size = 255; > | > | input_report_abs(idev, ABS_MT_TOUCH_MAJOR, touch_size); > | input_report_abs(idev, ABS_MT_PRESSURE, touch_size); Okay, I also don't know how to test it properly. If anyone knows, please let us know :)
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 887888c53996..eadd26d5a06f 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -65,6 +65,7 @@ #define CMD_HEADER_REK 0x66 /* FW position data */ +#define PACKET_SIZE_OLD 40 #define PACKET_SIZE 55 #define MAX_CONTACT_NUM 10 #define FW_POS_HEADER 0 @@ -792,7 +793,8 @@ static int elants_i2c_fw_update(struct elants_data *ts) * Event reporting. */ -static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) +static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf, + size_t report_len) { struct input_dev *input = ts->input; unsigned int n_fingers; @@ -804,7 +806,8 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) buf[FW_POS_STATE]; dev_dbg(&ts->client->dev, - "n_fingers: %u, state: %04x\n", n_fingers, finger_state); + "n_fingers: %u, state: %04x, report_len: %zu\n", + n_fingers, finger_state, report_len); for (i = 0; i < MAX_CONTACT_NUM && n_fingers; i++) { if (finger_state & 1) { @@ -814,8 +817,16 @@ static void elants_i2c_mt_event(struct elants_data *ts, u8 *buf) pos = &buf[FW_POS_XY + i * 3]; x = (((u16)pos[0] & 0xf0) << 4) | pos[1]; y = (((u16)pos[0] & 0x0f) << 8) | pos[2]; - p = buf[FW_POS_PRESSURE + i]; - w = buf[FW_POS_WIDTH + i]; + if (report_len == PACKET_SIZE_OLD) { + w = buf[FW_POS_WIDTH + i / 2]; + w >>= 4 * (~i & 1); // little-endian-nibbles + w |= w << 4; + w |= !w; + p = w; + } else { + p = buf[FW_POS_PRESSURE + i]; + w = buf[FW_POS_WIDTH + i]; + } dev_dbg(&ts->client->dev, "i=%d x=%d y=%d p=%d w=%d\n", i, x, y, p, w); @@ -848,7 +859,8 @@ static u8 elants_i2c_calculate_checksum(u8 *buf) return checksum; } -static void elants_i2c_event(struct elants_data *ts, u8 *buf) +static void elants_i2c_event(struct elants_data *ts, u8 *buf, + size_t report_len) { u8 checksum = elants_i2c_calculate_checksum(buf); @@ -862,7 +874,7 @@ static void elants_i2c_event(struct elants_data *ts, u8 *buf) "%s: unknown packet type: %02x\n", __func__, buf[FW_POS_HEADER]); else - elants_i2c_mt_event(ts, buf); + elants_i2c_mt_event(ts, buf, report_len); } static irqreturn_t elants_i2c_irq(int irq, void *_dev) @@ -920,7 +932,8 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) break; case QUEUE_HEADER_SINGLE: - elants_i2c_event(ts, &ts->buf[HEADER_SIZE]); + elants_i2c_event(ts, &ts->buf[HEADER_SIZE], + ts->buf[FW_HDR_LENGTH]); break; case QUEUE_HEADER_NORMAL: @@ -933,17 +946,18 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev) } report_len = ts->buf[FW_HDR_LENGTH] / report_count; - if (report_len != PACKET_SIZE) { + if (report_len != PACKET_SIZE && + report_len != PACKET_SIZE_OLD) { dev_err(&client->dev, - "mismatching report length: %*ph\n", + "unsupported report length: %*ph\n", HEADER_SIZE, ts->buf); break; } for (i = 0; i < report_count; i++) { u8 *buf = ts->buf + HEADER_SIZE + - i * PACKET_SIZE; - elants_i2c_event(ts, buf); + i * report_len; + elants_i2c_event(ts, buf, report_len); } break;
Support ELAN touchpad sensor with older firmware as found on eg. Asus Transformer Pads. Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl> --- drivers/input/touchscreen/elants_i2c.c | 36 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-)