Input: elan_i2c - add support for high resolution reports
authorJingle Wu <jingle.wu@emc.com.tw>
Mon, 20 Jul 2020 19:46:10 +0000 (12:46 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 21 Jul 2020 17:11:18 +0000 (10:11 -0700)
Newer controllers (identified as "pattern" version 2) send higher
resolution reports, with 16-bit X and Y coordinates (previous generations
used 12-bit values). These new high resolution reports use report ID of
0x60.

SMbus controllers use the same buffer size for both the new and old
reports, and because of that high resolution reports no longer carry
area of contact data with SMbus.

Signed-off-by: Jingle Wu <jingle.wu@emc.com.tw>
Link: https://lore.kernel.org/r/20200710054116.5529-1-jingle.wu@emc.com.tw
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/mouse/elan_i2c.h
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/elan_i2c_i2c.c
drivers/input/mouse/elan_i2c_smbus.c

index 6e6f13a4489cfad84d94bfa39e3991ac65350067..c75b00c45d7505657119df8c4d4ebe6666eb3529 100644 (file)
@@ -26,6 +26,8 @@
 
 #define ETP_CALIBRATE_MAX_LEN  3
 
+#define ETP_FEATURE_REPORT_MK  BIT(0)
+
 /* IAP Firmware handling */
 #define ETP_PRODUCT_ID_FORMAT_STRING   "%d.0"
 #define ETP_FW_NAME            "elan_i2c_" ETP_PRODUCT_ID_FORMAT_STRING ".bin"
@@ -82,7 +84,11 @@ struct elan_transport_ops {
        int (*finish_fw_update)(struct i2c_client *client,
                                struct completion *reset_done);
 
-       int (*get_report)(struct i2c_client *client, u8 *report);
+       int (*get_report_features)(struct i2c_client *client, u8 pattern,
+                                  unsigned int *features,
+                                  unsigned int *report_len);
+       int (*get_report)(struct i2c_client *client, u8 *report,
+                         unsigned int report_len);
        int (*get_pressure_adjustment)(struct i2c_client *client,
                                       int *adjustment);
        int (*get_pattern)(struct i2c_client *client, u8 *pattern);
index 3230aaf63dab15293a6fa929a717321e651a2c55..f10635a0b1897e9ef5040f99dc3c9c5fd3c8f3af 100644 (file)
 #define ETP_MAX_FINGERS                5
 #define ETP_FINGER_DATA_LEN    5
 #define ETP_REPORT_ID          0x5D
+#define ETP_REPORT_ID2         0x60    /* High precision report */
 #define ETP_TP_REPORT_ID       0x5E
 #define ETP_REPORT_ID_OFFSET   2
 #define ETP_TOUCH_INFO_OFFSET  3
 #define ETP_FINGER_DATA_OFFSET 4
 #define ETP_HOVER_INFO_OFFSET  30
-#define ETP_MAX_REPORT_LEN     34
+#define ETP_MK_DATA_OFFSET     33      /* For high precision reports */
+#define ETP_MAX_REPORT_LEN     39
 
 /* The main device structure */
 struct elan_tp_data {
@@ -85,6 +87,8 @@ struct elan_tp_data {
        u8                      sm_version;
        u8                      iap_version;
        u16                     fw_checksum;
+       unsigned int            report_features;
+       unsigned int            report_len;
        int                     pressure_adjustment;
        u8                      mode;
        u16                     ic_type;
@@ -359,6 +363,12 @@ static int elan_query_device_info(struct elan_tp_data *data)
        if (error)
                return error;
 
+       error = data->ops->get_report_features(data->client, data->pattern,
+                                              &data->report_features,
+                                              &data->report_len);
+       if (error)
+               return error;
+
        error = elan_get_fwinfo(data->ic_type, data->iap_version,
                                &data->fw_validpage_count,
                                &data->fw_signature_address,
@@ -371,16 +381,21 @@ static int elan_query_device_info(struct elan_tp_data *data)
        return 0;
 }
 
-static unsigned int elan_convert_resolution(u8 val)
+static unsigned int elan_convert_resolution(u8 val, u8 pattern)
 {
        /*
-        * (value from firmware) * 10 + 790 = dpi
-        *
+        * pattern <= 0x01:
+        *      (value from firmware) * 10 + 790 = dpi
+        * else
+        *      ((value from firmware) + 3) * 100 = dpi
+        */
+       int res = pattern <= 0x01 ?
+               (int)(char)val * 10 + 790 : ((int)(char)val + 3) * 100;
+       /*
         * We also have to convert dpi to dots/mm (*10/254 to avoid floating
         * point).
         */
-
-       return ((int)(char)val * 10 + 790) * 10 / 254;
+       return res * 10 / 254;
 }
 
 static int elan_query_device_parameters(struct elan_tp_data *data)
@@ -429,8 +444,8 @@ static int elan_query_device_parameters(struct elan_tp_data *data)
                if (error)
                        return error;
 
-               data->x_res = elan_convert_resolution(hw_x_res);
-               data->y_res = elan_convert_resolution(hw_y_res);
+               data->x_res = elan_convert_resolution(hw_x_res, data->pattern);
+               data->y_res = elan_convert_resolution(hw_y_res, data->pattern);
        } else {
                data->x_res = (data->max_x + 1) / x_mm;
                data->y_res = (data->max_y + 1) / y_mm;
@@ -908,24 +923,22 @@ static const struct attribute_group *elan_sysfs_groups[] = {
  * Elan isr functions
  ******************************************************************
  */
-static void elan_report_contact(struct elan_tp_data *data,
-                               int contact_num, bool contact_valid,
-                               u8 *finger_data)
+static void elan_report_contact(struct elan_tp_data *data, int contact_num,
+                               bool contact_valid, bool high_precision,
+                               u8 *packet, u8 *finger_data)
 {
        struct input_dev *input = data->input;
        unsigned int pos_x, pos_y;
-       unsigned int pressure, mk_x, mk_y;
-       unsigned int area_x, area_y, major, minor;
-       unsigned int scaled_pressure;
+       unsigned int pressure, scaled_pressure;
 
        if (contact_valid) {
-               pos_x = ((finger_data[0] & 0xf0) << 4) |
-                                               finger_data[1];
-               pos_y = ((finger_data[0] & 0x0f) << 8) |
-                                               finger_data[2];
-               mk_x = (finger_data[3] & 0x0f);
-               mk_y = (finger_data[3] >> 4);
-               pressure = finger_data[4];
+               if (high_precision) {
+                       pos_x = get_unaligned_be16(&finger_data[0]);
+                       pos_y = get_unaligned_be16(&finger_data[2]);
+               } else {
+                       pos_x = ((finger_data[0] & 0xf0) << 4) | finger_data[1];
+                       pos_y = ((finger_data[0] & 0x0f) << 8) | finger_data[2];
+               }
 
                if (pos_x > data->max_x || pos_y > data->max_y) {
                        dev_dbg(input->dev.parent,
@@ -935,18 +948,8 @@ static void elan_report_contact(struct elan_tp_data *data,
                        return;
                }
 
-               /*
-                * To avoid treating large finger as palm, let's reduce the
-                * width x and y per trace.
-                */
-               area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
-               area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
-
-               major = max(area_x, area_y);
-               minor = min(area_x, area_y);
-
+               pressure = finger_data[4];
                scaled_pressure = pressure + data->pressure_adjustment;
-
                if (scaled_pressure > ETP_MAX_PRESSURE)
                        scaled_pressure = ETP_MAX_PRESSURE;
 
@@ -955,16 +958,37 @@ static void elan_report_contact(struct elan_tp_data *data,
                input_report_abs(input, ABS_MT_POSITION_X, pos_x);
                input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y);
                input_report_abs(input, ABS_MT_PRESSURE, scaled_pressure);
-               input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
-               input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
-               input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+
+               if (data->report_features & ETP_FEATURE_REPORT_MK) {
+                       unsigned int mk_x, mk_y, area_x, area_y;
+                       u8 mk_data = high_precision ?
+                               packet[ETP_MK_DATA_OFFSET + contact_num] :
+                               finger_data[3];
+
+                       mk_x = mk_data & 0x0f;
+                       mk_y = mk_data >> 4;
+
+                       /*
+                        * To avoid treating large finger as palm, let's reduce
+                        * the width x and y per trace.
+                        */
+                       area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
+                       area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
+
+                       input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
+                       input_report_abs(input, ABS_MT_TOUCH_MAJOR,
+                                        max(area_x, area_y));
+                       input_report_abs(input, ABS_MT_TOUCH_MINOR,
+                                        min(area_x, area_y));
+               }
        } else {
                input_mt_slot(input, contact_num);
                input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
        }
 }
 
-static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
+static void elan_report_absolute(struct elan_tp_data *data, u8 *packet,
+                                bool high_precision)
 {
        struct input_dev *input = data->input;
        u8 *finger_data = &packet[ETP_FINGER_DATA_OFFSET];
@@ -973,11 +997,12 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
        u8 hover_info = packet[ETP_HOVER_INFO_OFFSET];
        bool contact_valid, hover_event;
 
-       hover_event = hover_info & 0x40;
-       for (i = 0; i < ETP_MAX_FINGERS; i++) {
-               contact_valid = tp_info & (1U << (3 + i));
-               elan_report_contact(data, i, contact_valid, finger_data);
+       hover_event = hover_info & BIT(6);
 
+       for (i = 0; i < ETP_MAX_FINGERS; i++) {
+               contact_valid = tp_info & BIT(3 + i);
+               elan_report_contact(data, i, contact_valid, high_precision,
+                                   packet, finger_data);
                if (contact_valid)
                        finger_data += ETP_FINGER_DATA_LEN;
        }
@@ -1034,7 +1059,7 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
                goto out;
        }
 
-       error = data->ops->get_report(data->client, report);
+       error = data->ops->get_report(data->client, report, data->report_len);
        if (error)
                goto out;
 
@@ -1042,7 +1067,10 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
 
        switch (report[ETP_REPORT_ID_OFFSET]) {
        case ETP_REPORT_ID:
-               elan_report_absolute(data, report);
+               elan_report_absolute(data, report, false);
+               break;
+       case ETP_REPORT_ID2:
+               elan_report_absolute(data, report, true);
                break;
        case ETP_TP_REPORT_ID:
                elan_report_trackpoint(data, report);
@@ -1133,7 +1161,9 @@ static int elan_setup_input_device(struct elan_tp_data *data)
        input_abs_set_res(input, ABS_X, data->x_res);
        input_abs_set_res(input, ABS_Y, data->y_res);
        input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
-       input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0);
+       if (data->report_features & ETP_FEATURE_REPORT_MK)
+               input_set_abs_params(input, ABS_TOOL_WIDTH,
+                                    0, ETP_FINGER_WIDTH, 0, 0);
        input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0);
 
        /* And MT parameters */
@@ -1143,10 +1173,12 @@ static int elan_setup_input_device(struct elan_tp_data *data)
        input_abs_set_res(input, ABS_MT_POSITION_Y, data->y_res);
        input_set_abs_params(input, ABS_MT_PRESSURE, 0,
                             ETP_MAX_PRESSURE, 0, 0);
-       input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
-                            ETP_FINGER_WIDTH * max_width, 0, 0);
-       input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
-                            ETP_FINGER_WIDTH * min_width, 0, 0);
+       if (data->report_features & ETP_FEATURE_REPORT_MK) {
+               input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+                                    0, ETP_FINGER_WIDTH * max_width, 0, 0);
+               input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
+                                    0, ETP_FINGER_WIDTH * min_width, 0, 0);
+       }
 
        data->input = input;
 
index b84e3bbe62b5bea8df7ee7774625bfb09136a0e3..5a496d4ffa4911fb15dc570c4b686d1223458297 100644 (file)
@@ -60,6 +60,8 @@
 #define ETP_I2C_IAP_TYPE_CMD           0x0304
 
 #define ETP_I2C_REPORT_LEN             34
+#define ETP_I2C_REPORT_LEN_ID2         39
+#define ETP_I2C_REPORT_MAX_LEN         39
 #define ETP_I2C_DESC_LENGTH            30
 #define ETP_I2C_REPORT_DESC_LENGTH     158
 #define ETP_I2C_INF_LENGTH             2
@@ -394,7 +396,7 @@ static int elan_i2c_get_max(struct i2c_client *client,
                return error;
        }
 
-       *max_x = le16_to_cpup((__le16 *)val) & 0x0fff;
+       *max_x = le16_to_cpup((__le16 *)val);
 
        error = elan_i2c_read_cmd(client, ETP_I2C_MAX_Y_AXIS_CMD, val);
        if (error) {
@@ -402,7 +404,7 @@ static int elan_i2c_get_max(struct i2c_client *client,
                return error;
        }
 
-       *max_y = le16_to_cpup((__le16 *)val) & 0x0fff;
+       *max_y = le16_to_cpup((__le16 *)val);
 
        return 0;
 }
@@ -674,12 +676,12 @@ static int elan_i2c_finish_fw_update(struct i2c_client *client,
                                     struct completion *completion)
 {
        struct device *dev = &client->dev;
-       int error;
+       int error = 0;
        int len;
-       u8 buffer[ETP_I2C_REPORT_LEN];
+       u8 buffer[ETP_I2C_REPORT_MAX_LEN];
 
-       len = i2c_master_recv(client, buffer, ETP_I2C_REPORT_LEN);
-       if (len != ETP_I2C_REPORT_LEN) {
+       len = i2c_master_recv(client, buffer, ETP_I2C_REPORT_MAX_LEN);
+       if (len <= 0) {
                error = len < 0 ? len : -EIO;
                dev_warn(dev, "failed to read I2C data after FW WDT reset: %d (%d)\n",
                        error, len);
@@ -713,20 +715,31 @@ static int elan_i2c_finish_fw_update(struct i2c_client *client,
        return 0;
 }
 
-static int elan_i2c_get_report(struct i2c_client *client, u8 *report)
+static int elan_i2c_get_report_features(struct i2c_client *client, u8 pattern,
+                                       unsigned int *features,
+                                       unsigned int *report_len)
+{
+       *features = ETP_FEATURE_REPORT_MK;
+       *report_len = pattern <= 0x01 ?
+                       ETP_I2C_REPORT_LEN : ETP_I2C_REPORT_LEN_ID2;
+       return 0;
+}
+
+static int elan_i2c_get_report(struct i2c_client *client,
+                              u8 *report, unsigned int report_len)
 {
        int len;
 
-       len = i2c_master_recv(client, report, ETP_I2C_REPORT_LEN);
+       len = i2c_master_recv(client, report, report_len);
        if (len < 0) {
                dev_err(&client->dev, "failed to read report data: %d\n", len);
                return len;
        }
 
-       if (len != ETP_I2C_REPORT_LEN) {
+       if (len != report_len) {
                dev_err(&client->dev,
                        "wrong report length (%d vs %d expected)\n",
-                       len, ETP_I2C_REPORT_LEN);
+                       len, report_len);
                return -EIO;
        }
 
@@ -763,5 +776,6 @@ const struct elan_transport_ops elan_i2c_ops = {
 
        .get_pattern            = elan_i2c_get_pattern,
 
+       .get_report_features    = elan_i2c_get_report_features,
        .get_report             = elan_i2c_get_report,
 };
index f7af8b7b345f32eb4d3b0688aae21fe9165f3e17..8ff823751f3ba05fa918e6f2aa9c7cb1884438f0 100644 (file)
@@ -469,7 +469,21 @@ static int elan_smbus_write_fw_block(struct i2c_client *client, u16 fw_page_size
        return 0;
 }
 
-static int elan_smbus_get_report(struct i2c_client *client, u8 *report)
+static int elan_smbus_get_report_features(struct i2c_client *client, u8 pattern,
+                                         unsigned int *features,
+                                         unsigned int *report_len)
+{
+       /*
+        * SMBus controllers with pattern 2 lack area info, as newer
+        * high-precision packets use that space for coordinates.
+        */
+       *features = pattern <= 0x01 ? ETP_FEATURE_REPORT_MK : 0;
+       *report_len = ETP_SMBUS_REPORT_LEN;
+       return 0;
+}
+
+static int elan_smbus_get_report(struct i2c_client *client,
+                                u8 *report, unsigned int report_len)
 {
        int len;
 
@@ -534,6 +548,7 @@ const struct elan_transport_ops elan_smbus_ops = {
        .write_fw_block         = elan_smbus_write_fw_block,
        .finish_fw_update       = elan_smbus_finish_fw_update,
 
+       .get_report_features    = elan_smbus_get_report_features,
        .get_report             = elan_smbus_get_report,
        .get_pattern            = elan_smbus_get_pattern,
 };