Merge branch 'for-linus' into next
[linux-2.6-microblaze.git] / drivers / input / mouse / elan_i2c_core.c
index 8719da5..8a0f224 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,11 +87,14 @@ 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;
        u16                     fw_validpage_count;
-       u16                     fw_signature_address;
+       u16                     fw_page_size;
+       u32                     fw_signature_address;
 
        bool                    irq_wake;
 
@@ -100,8 +105,8 @@ struct elan_tp_data {
        bool                    middle_button;
 };
 
-static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
-                          u16 *signature_address)
+static int elan_get_fwinfo(u16 ic_type, u8 iap_version, u16 *validpage_count,
+                          u32 *signature_address, u16 *page_size)
 {
        switch (ic_type) {
        case 0x00:
@@ -126,16 +131,36 @@ static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
        case 0x10:
                *validpage_count = 1024;
                break;
+       case 0x11:
+               *validpage_count = 1280;
+               break;
+       case 0x13:
+               *validpage_count = 2048;
+               break;
+       case 0x14:
+               *validpage_count = 1024;
+               break;
        default:
                /* unknown ic type clear value */
                *validpage_count = 0;
                *signature_address = 0;
+               *page_size = 0;
                return -ENXIO;
        }
 
        *signature_address =
                (*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
 
+       if (ic_type == 0x14 && iap_version >= 2) {
+               *validpage_count /= 8;
+               *page_size = ETP_FW_PAGE_SIZE_512;
+       } else if (ic_type >= 0x0D && iap_version >= 1) {
+               *validpage_count /= 2;
+               *page_size = ETP_FW_PAGE_SIZE_128;
+       } else {
+               *page_size = ETP_FW_PAGE_SIZE;
+       }
+
        return 0;
 }
 
@@ -215,8 +240,13 @@ static int elan_query_product(struct elan_tp_data *data)
        if (error)
                return error;
 
-       error = data->ops->get_sm_version(data->client, &data->ic_type,
-                                         &data->sm_version, &data->clickpad);
+       error = data->ops->get_pattern(data->client, &data->pattern);
+       if (error)
+               return error;
+
+       error = data->ops->get_sm_version(data->client, data->pattern,
+                                         &data->ic_type, &data->sm_version,
+                                         &data->clickpad);
        if (error)
                return error;
 
@@ -312,9 +342,9 @@ static int elan_initialize(struct elan_tp_data *data)
 static int elan_query_device_info(struct elan_tp_data *data)
 {
        int error;
-       u16 ic_type;
 
-       error = data->ops->get_version(data->client, false, &data->fw_version);
+       error = data->ops->get_version(data->client, data->pattern, false,
+                                      &data->fw_version);
        if (error)
                return error;
 
@@ -323,7 +353,8 @@ static int elan_query_device_info(struct elan_tp_data *data)
        if (error)
                return error;
 
-       error = data->ops->get_version(data->client, true, &data->iap_version);
+       error = data->ops->get_version(data->client, data->pattern,
+                                      true, &data->iap_version);
        if (error)
                return error;
 
@@ -332,17 +363,16 @@ static int elan_query_device_info(struct elan_tp_data *data)
        if (error)
                return error;
 
-       error = data->ops->get_pattern(data->client, &data->pattern);
+       error = data->ops->get_report_features(data->client, data->pattern,
+                                              &data->report_features,
+                                              &data->report_len);
        if (error)
                return error;
 
-       if (data->pattern == 0x01)
-               ic_type = data->ic_type;
-       else
-               ic_type = data->iap_version;
-
-       error = elan_get_fwinfo(ic_type, &data->fw_validpage_count,
-                               &data->fw_signature_address);
+       error = elan_get_fwinfo(data->ic_type, data->iap_version,
+                               &data->fw_validpage_count,
+                               &data->fw_signature_address,
+                               &data->fw_page_size);
        if (error)
                dev_warn(&data->client->dev,
                         "unexpected iap version %#04x (ic type: %#04x), firmware update will not work\n",
@@ -351,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)
@@ -409,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;
@@ -430,14 +465,14 @@ static int elan_query_device_parameters(struct elan_tp_data *data)
  * IAP firmware updater related routines
  **********************************************************
  */
-static int elan_write_fw_block(struct elan_tp_data *data,
+static int elan_write_fw_block(struct elan_tp_data *data, u16 page_size,
                               const u8 *page, u16 checksum, int idx)
 {
        int retry = ETP_RETRY_COUNT;
        int error;
 
        do {
-               error = data->ops->write_fw_block(data->client,
+               error = data->ops->write_fw_block(data->client, page_size,
                                                  page, checksum, idx);
                if (!error)
                        return 0;
@@ -460,21 +495,23 @@ static int __elan_update_firmware(struct elan_tp_data *data,
        u16 boot_page_count;
        u16 sw_checksum = 0, fw_checksum = 0;
 
-       error = data->ops->prepare_fw_update(client);
+       error = data->ops->prepare_fw_update(client, data->ic_type,
+                                            data->iap_version);
        if (error)
                return error;
 
        iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
 
-       boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
+       boot_page_count = (iap_start_addr * 2) / data->fw_page_size;
        for (i = boot_page_count; i < data->fw_validpage_count; i++) {
                u16 checksum = 0;
-               const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
+               const u8 *page = &fw->data[i * data->fw_page_size];
 
-               for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
+               for (j = 0; j < data->fw_page_size; j += 2)
                        checksum += ((page[j + 1] << 8) | page[j]);
 
-               error = elan_write_fw_block(data, page, checksum, i);
+               error = elan_write_fw_block(data, data->fw_page_size,
+                                           page, checksum, i);
                if (error) {
                        dev_err(dev, "write page %d fail: %d\n", i, error);
                        return error;
@@ -886,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,
@@ -913,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;
 
@@ -933,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);
+               input_mt_report_slot_inactive(input);
        }
 }
 
-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];
@@ -951,11 +997,14 @@ 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);
+       pm_wakeup_event(&data->client->dev, 0);
+
+       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;
        }
@@ -974,6 +1023,8 @@ static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
        u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
        int x, y;
 
+       pm_wakeup_event(&data->client->dev, 0);
+
        if (!data->tp_input) {
                dev_warn_once(&data->client->dev,
                              "received a trackpoint report while no trackpoint device has been created. Please report upstream.\n");
@@ -998,7 +1049,6 @@ static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
 static irqreturn_t elan_isr(int irq, void *dev_id)
 {
        struct elan_tp_data *data = dev_id;
-       struct device *dev = &data->client->dev;
        int error;
        u8 report[ETP_MAX_REPORT_LEN];
 
@@ -1012,21 +1062,22 @@ 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;
 
-       pm_wakeup_event(dev, 0);
-
        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);
                break;
        default:
-               dev_err(dev, "invalid report id data (%x)\n",
+               dev_err(&data->client->dev, "invalid report id data (%x)\n",
                        report[ETP_REPORT_ID_OFFSET]);
        }
 
@@ -1111,7 +1162,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 */
@@ -1121,10 +1174,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;