Commit bd3b8480 authored by Yauhen Kharuzhy's avatar Yauhen Kharuzhy Committed by Dmitry Torokhov

Input: goodix - add support for 9-bytes reports

Some variants of Goodix touchscreen firmwares use 9-bytes finger
report format instead of common 8-bytes format.

This report format may be present as:

struct goodix_contact_data {
        uint8_t unknown1;
        uint8_t track_id;
        uint8_t unknown2;
        uint16_t x;
        uint16_t y;
        uint16_t w;
}__attribute__((packed));

Add support for such format and use it for Lenovo Yoga Book notebook
(which uses a Goodix touchpad as a touch keyboard).
Signed-off-by: default avatarYauhen Kharuzhy <jekhor@gmail.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent afce285b
...@@ -53,6 +53,7 @@ struct goodix_ts_data { ...@@ -53,6 +53,7 @@ struct goodix_ts_data {
const char *cfg_name; const char *cfg_name;
struct completion firmware_loading_complete; struct completion firmware_loading_complete;
unsigned long irq_flags; unsigned long irq_flags;
unsigned int contact_size;
}; };
#define GOODIX_GPIO_INT_NAME "irq" #define GOODIX_GPIO_INT_NAME "irq"
...@@ -62,6 +63,7 @@ struct goodix_ts_data { ...@@ -62,6 +63,7 @@ struct goodix_ts_data {
#define GOODIX_MAX_WIDTH 4096 #define GOODIX_MAX_WIDTH 4096
#define GOODIX_INT_TRIGGER 1 #define GOODIX_INT_TRIGGER 1
#define GOODIX_CONTACT_SIZE 8 #define GOODIX_CONTACT_SIZE 8
#define GOODIX_MAX_CONTACT_SIZE 9
#define GOODIX_MAX_CONTACTS 10 #define GOODIX_MAX_CONTACTS 10
#define GOODIX_CONFIG_MAX_LENGTH 240 #define GOODIX_CONFIG_MAX_LENGTH 240
...@@ -144,6 +146,19 @@ static const struct dmi_system_id rotated_screen[] = { ...@@ -144,6 +146,19 @@ static const struct dmi_system_id rotated_screen[] = {
{} {}
}; };
static const struct dmi_system_id nine_bytes_report[] = {
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
.ident = "Lenovo YogaBook",
/* YB1-X91L/F and YB1-X90L/F */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9")
}
},
#endif
{}
};
/** /**
* goodix_i2c_read - read data from a register of the i2c slave device. * goodix_i2c_read - read data from a register of the i2c slave device.
* *
...@@ -249,7 +264,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) ...@@ -249,7 +264,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
max_timeout = jiffies + msecs_to_jiffies(GOODIX_BUFFER_STATUS_TIMEOUT); max_timeout = jiffies + msecs_to_jiffies(GOODIX_BUFFER_STATUS_TIMEOUT);
do { do {
error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR,
data, GOODIX_CONTACT_SIZE + 1); data, ts->contact_size + 1);
if (error) { if (error) {
dev_err(&ts->client->dev, "I2C transfer error: %d\n", dev_err(&ts->client->dev, "I2C transfer error: %d\n",
error); error);
...@@ -262,12 +277,12 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) ...@@ -262,12 +277,12 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
return -EPROTO; return -EPROTO;
if (touch_num > 1) { if (touch_num > 1) {
data += 1 + GOODIX_CONTACT_SIZE; data += 1 + ts->contact_size;
error = goodix_i2c_read(ts->client, error = goodix_i2c_read(ts->client,
GOODIX_READ_COOR_ADDR + GOODIX_READ_COOR_ADDR +
1 + GOODIX_CONTACT_SIZE, 1 + ts->contact_size,
data, data,
GOODIX_CONTACT_SIZE * ts->contact_size *
(touch_num - 1)); (touch_num - 1));
if (error) if (error)
return error; return error;
...@@ -286,7 +301,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) ...@@ -286,7 +301,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
return 0; return 0;
} }
static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data)
{ {
int id = coor_data[0] & 0x0F; int id = coor_data[0] & 0x0F;
int input_x = get_unaligned_le16(&coor_data[1]); int input_x = get_unaligned_le16(&coor_data[1]);
...@@ -301,6 +316,21 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) ...@@ -301,6 +316,21 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
} }
static void goodix_ts_report_touch_9b(struct goodix_ts_data *ts, u8 *coor_data)
{
int id = coor_data[1] & 0x0F;
int input_x = get_unaligned_le16(&coor_data[3]);
int input_y = get_unaligned_le16(&coor_data[5]);
int input_w = get_unaligned_le16(&coor_data[7]);
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
touchscreen_report_pos(ts->input_dev, &ts->prop,
input_x, input_y, true);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
}
/** /**
* goodix_process_events - Process incoming events * goodix_process_events - Process incoming events
* *
...@@ -311,7 +341,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) ...@@ -311,7 +341,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
*/ */
static void goodix_process_events(struct goodix_ts_data *ts) static void goodix_process_events(struct goodix_ts_data *ts)
{ {
u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; u8 point_data[1 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
int touch_num; int touch_num;
int i; int i;
...@@ -326,8 +356,12 @@ static void goodix_process_events(struct goodix_ts_data *ts) ...@@ -326,8 +356,12 @@ static void goodix_process_events(struct goodix_ts_data *ts)
input_report_key(ts->input_dev, KEY_LEFTMETA, point_data[0] & BIT(4)); input_report_key(ts->input_dev, KEY_LEFTMETA, point_data[0] & BIT(4));
for (i = 0; i < touch_num; i++) for (i = 0; i < touch_num; i++)
goodix_ts_report_touch(ts, if (ts->contact_size == 9)
&point_data[1 + GOODIX_CONTACT_SIZE * i]); goodix_ts_report_touch_9b(ts,
&point_data[1 + ts->contact_size * i]);
else
goodix_ts_report_touch_8b(ts,
&point_data[1 + ts->contact_size * i]);
input_mt_sync_frame(ts->input_dev); input_mt_sync_frame(ts->input_dev);
input_sync(ts->input_dev); input_sync(ts->input_dev);
...@@ -730,6 +764,13 @@ static int goodix_configure_dev(struct goodix_ts_data *ts) ...@@ -730,6 +764,13 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
"Applying '180 degrees rotated screen' quirk\n"); "Applying '180 degrees rotated screen' quirk\n");
} }
if (dmi_check_system(nine_bytes_report)) {
ts->contact_size = 9;
dev_dbg(&ts->client->dev,
"Non-standard 9-bytes report format quirk\n");
}
error = input_mt_init_slots(ts->input_dev, ts->max_touch_num, error = input_mt_init_slots(ts->input_dev, ts->max_touch_num,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) { if (error) {
...@@ -810,6 +851,7 @@ static int goodix_ts_probe(struct i2c_client *client, ...@@ -810,6 +851,7 @@ static int goodix_ts_probe(struct i2c_client *client,
ts->client = client; ts->client = client;
i2c_set_clientdata(client, ts); i2c_set_clientdata(client, ts);
init_completion(&ts->firmware_loading_complete); init_completion(&ts->firmware_loading_complete);
ts->contact_size = GOODIX_CONTACT_SIZE;
error = goodix_get_gpio_config(ts); error = goodix_get_gpio_config(ts);
if (error) if (error)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment