]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
Input: goodix - add support for 9-bytes reports
authorYauhen Kharuzhy <jekhor@gmail.com>
Mon, 16 Sep 2019 23:32:24 +0000 (16:32 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 2 Oct 2019 21:13:27 +0000 (14:13 -0700)
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: Yauhen Kharuzhy <jekhor@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/touchscreen/goodix.c

index 5178ea8b5f3099eee52a8483e72fa0b2dcce679b..fb43aa708660632613013c936b937bfe9991f46e 100644 (file)
@@ -53,6 +53,7 @@ struct goodix_ts_data {
        const char *cfg_name;
        struct completion firmware_loading_complete;
        unsigned long irq_flags;
+       unsigned int contact_size;
 };
 
 #define GOODIX_GPIO_INT_NAME           "irq"
@@ -62,6 +63,7 @@ struct goodix_ts_data {
 #define GOODIX_MAX_WIDTH               4096
 #define GOODIX_INT_TRIGGER             1
 #define GOODIX_CONTACT_SIZE            8
+#define GOODIX_MAX_CONTACT_SIZE                9
 #define GOODIX_MAX_CONTACTS            10
 
 #define GOODIX_CONFIG_MAX_LENGTH       240
@@ -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.
  *
@@ -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);
        do {
                error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR,
-                                       data, GOODIX_CONTACT_SIZE + 1);
+                                       data, ts->contact_size + 1);
                if (error) {
                        dev_err(&ts->client->dev, "I2C transfer error: %d\n",
                                        error);
@@ -262,12 +277,12 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
                                return -EPROTO;
 
                        if (touch_num > 1) {
-                               data += 1 + GOODIX_CONTACT_SIZE;
+                               data += 1 + ts->contact_size;
                                error = goodix_i2c_read(ts->client,
                                                GOODIX_READ_COOR_ADDR +
-                                                       1 + GOODIX_CONTACT_SIZE,
+                                                       1 + ts->contact_size,
                                                data,
-                                               GOODIX_CONTACT_SIZE *
+                                               ts->contact_size *
                                                        (touch_num - 1));
                                if (error)
                                        return error;
@@ -286,7 +301,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
        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 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)
        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
  *
@@ -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)
 {
-       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 i;
 
@@ -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));
 
        for (i = 0; i < touch_num; i++)
-               goodix_ts_report_touch(ts,
-                               &point_data[1 + GOODIX_CONTACT_SIZE * i]);
+               if (ts->contact_size == 9)
+                       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_sync(ts->input_dev);
@@ -730,6 +764,13 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
                        "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,
                                    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
        if (error) {
@@ -810,6 +851,7 @@ static int goodix_ts_probe(struct i2c_client *client,
        ts->client = client;
        i2c_set_clientdata(client, ts);
        init_completion(&ts->firmware_loading_complete);
+       ts->contact_size = GOODIX_CONTACT_SIZE;
 
        error = goodix_get_gpio_config(ts);
        if (error)