Input: cyapa - add proximity support for gen5 and gen6 modules
authorDudley Du <dudl@cypress.com>
Mon, 20 Jul 2015 23:57:53 +0000 (16:57 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 24 Jul 2015 00:34:05 +0000 (17:34 -0700)
Gen5 and Gen6 trackpad devices are able to detect and report object
proximity data/events, add this function support in the cyapa driver
through the ABS_DISTANCE event.

Signed-off-by: Dudley Du <dudl@cypress.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/mouse/cyapa.c
drivers/input/mouse/cyapa.h
drivers/input/mouse/cyapa_gen3.c
drivers/input/mouse/cyapa_gen5.c
drivers/input/mouse/cyapa_gen6.c

index 6952ed1c92c7bdf89475a52eb75695c8d8671ff1..2541fbb435335a220d062300da838cde8266c595 100644 (file)
@@ -477,6 +477,7 @@ static int cyapa_create_input_dev(struct cyapa *cyapa)
        if (cyapa->gen >= CYAPA_GEN5) {
                input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
                input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0);
+               input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0);
        }
 
        input_abs_set_res(input, ABS_MT_POSITION_X,
@@ -1340,6 +1341,13 @@ static int __maybe_unused cyapa_suspend(struct device *dev)
                                        error);
        }
 
+       /*
+        * Disable proximity interrupt when system idle, want true touch to
+        * wake the system.
+        */
+       if (cyapa->dev_pwr_mode != PWR_MODE_OFF)
+               cyapa->ops->set_proximity(cyapa, false);
+
        if (device_may_wakeup(dev))
                cyapa->irq_wake = (enable_irq_wake(client->irq) == 0);
 
@@ -1360,7 +1368,10 @@ static int __maybe_unused cyapa_resume(struct device *dev)
                cyapa->irq_wake = false;
        }
 
-       /* Update device states and runtime PM states. */
+       /*
+        * Update device states and runtime PM states.
+        * Re-Enable proximity interrupt after enter operational mode.
+        */
        error = cyapa_reinitialize(cyapa);
        if (error)
                dev_warn(dev, "failed to reinitialize TP device: %d\n", error);
index 3a211c0769e9c6835cdaa58b9ea7bd2d552e80fb..f51deb69c37050018173b84ae3121ba12c1a70d4 100644 (file)
@@ -274,6 +274,8 @@ struct cyapa_dev_ops {
                        u8 *, int *, cb_sort);
 
        int (*set_power_mode)(struct cyapa *, u8, u16);
+
+       int (*set_proximity)(struct cyapa *, bool);
 };
 
 struct cyapa_pip_cmd_states {
@@ -415,6 +417,7 @@ int cyapa_pip_bl_deactivate(struct cyapa *cyapa);
 ssize_t cyapa_pip_do_calibrate(struct device *dev,
                               struct device_attribute *attr,
                               const char *buf, size_t count);
+int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable);
 
 bool cyapa_pip_irq_cmd_handler(struct cyapa *cyapa);
 int cyapa_pip_irq_handler(struct cyapa *cyapa);
index 3884311410f7564276abdb1e3bd4654db0afd395..e7bbfee8b5d63a09dfc134e3ca470e73ca58e02d 100644 (file)
@@ -999,6 +999,11 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
        return ret;
 }
 
+static int cyapa_gen3_set_proximity(struct cyapa *cyapa, bool enable)
+{
+       return -EOPNOTSUPP;
+}
+
 static int cyapa_gen3_get_query_data(struct cyapa *cyapa)
 {
        u8 query_data[QUERY_DATA_SIZE];
@@ -1243,4 +1248,6 @@ const struct cyapa_dev_ops cyapa_gen3_ops = {
        .irq_cmd_handler = cyapa_gen3_irq_cmd_handler,
        .sort_empty_output_data = cyapa_gen3_empty_output_data,
        .set_power_mode = cyapa_gen3_set_power_mode,
+
+       .set_proximity = cyapa_gen3_set_proximity,
 };
index 4e19dce1dd62ddff399f2cf74c83e3aef19a8394..fa135f855f348121981d0afa2bd295c604d2e5d4 100644 (file)
 #define PIP_WAKEUP_EVENT_REPORT_ID  0x04
 #define PIP_PUSH_BTN_REPORT_ID      0x06
 #define GEN5_OLD_PUSH_BTN_REPORT_ID 0x05  /* Special for old Gen5 TP. */
+#define PIP_PROXIMITY_REPORT_ID     0x07
+
+#define PIP_PROXIMITY_REPORT_SIZE      6
+#define PIP_PROXIMITY_DISTANCE_OFFSET  0x05
+#define PIP_PROXIMITY_DISTANCE_MASK    0x01
 
 #define PIP_TOUCH_REPORT_HEAD_SIZE     7
 #define PIP_TOUCH_REPORT_MAX_SIZE      127
@@ -78,6 +83,8 @@
 #define PIP_SENSING_MODE_MUTUAL_CAP_FINE   0x00
 #define PIP_SENSING_MODE_SELF_CAP          0x02
 
+#define PIP_SET_PROXIMITY      0x49
+
 /* Macro of Gen5 */
 #define GEN5_BL_MAX_OUTPUT_LENGTH     0x0100
 #define GEN5_APP_MAX_OUTPUT_LENGTH    0x00fe
@@ -1517,6 +1524,28 @@ static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa)
        return 0;
 }
 
+int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable)
+{
+       u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, PIP_SET_PROXIMITY,
+                    (u8)!!enable
+       };
+       u8 resp_data[6];
+       int resp_len;
+       int error;
+
+       resp_len = sizeof(resp_data);
+       error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
+                       resp_data, &resp_len,
+                       500, cyapa_sort_tsg_pip_app_resp_data, false);
+       if (error || !VALID_CMD_RESP_HEADER(resp_data, PIP_SET_PROXIMITY) ||
+                       !PIP_CMD_COMPLETE_SUCCESS(resp_data)) {
+               error = (error == -ETIMEDOUT) ? -EOPNOTSUPP : error;
+               return error < 0 ? error : -EINVAL;
+       }
+
+       return 0;
+}
+
 int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state)
 {
        u8 cmd[] = { 0x05, 0x00, 0x00, 0x08};
@@ -2491,6 +2520,12 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
                        dev_warn(dev, "%s: failed to set power active mode.\n",
                                __func__);
 
+               /* By default, the trackpad proximity function is enabled. */
+               error = cyapa_pip_set_proximity(cyapa, true);
+               if (error)
+                       dev_warn(dev, "%s: failed to enable proximity.\n",
+                               __func__);
+
                /* Get trackpad product information. */
                error = cyapa_gen5_get_query_data(cyapa);
                if (error)
@@ -2607,6 +2642,17 @@ static void cyapa_pip_report_buttons(struct cyapa *cyapa,
        input_sync(input);
 }
 
+static void cyapa_pip_report_proximity(struct cyapa *cyapa,
+               const struct cyapa_pip_report_data *report_data)
+{
+       struct input_dev *input = cyapa->input;
+       u8 distance = report_data->report_head[PIP_PROXIMITY_DISTANCE_OFFSET] &
+                       PIP_PROXIMITY_DISTANCE_MASK;
+
+       input_report_abs(input, ABS_DISTANCE, distance);
+       input_sync(input);
+}
+
 static void cyapa_pip_report_slot_data(struct cyapa *cyapa,
                const struct cyapa_pip_touch_record *touch)
 {
@@ -2628,6 +2674,7 @@ static void cyapa_pip_report_slot_data(struct cyapa *cyapa,
                y = cyapa->max_abs_y - y;
        input_report_abs(input, ABS_MT_POSITION_X, x);
        input_report_abs(input, ABS_MT_POSITION_Y, y);
+       input_report_abs(input, ABS_DISTANCE, 0);
        input_report_abs(input, ABS_MT_PRESSURE,
                touch->z);
        input_report_abs(input, ABS_MT_TOUCH_MAJOR,
@@ -2715,7 +2762,8 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa)
        } else if (report_id != PIP_TOUCH_REPORT_ID &&
                        report_id != PIP_BTN_REPORT_ID &&
                        report_id != GEN5_OLD_PUSH_BTN_REPORT_ID &&
-                       report_id != PIP_PUSH_BTN_REPORT_ID) {
+                       report_id != PIP_PUSH_BTN_REPORT_ID &&
+                       report_id != PIP_PROXIMITY_REPORT_ID) {
                /* Running in BL mode or unknown response data read. */
                dev_err(dev, "invalid report_id=0x%02x\n", report_id);
                return -EINVAL;
@@ -2739,8 +2787,17 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa)
                return 0;
        }
 
+       if (report_id == PIP_PROXIMITY_REPORT_ID &&
+                       report_len != PIP_PROXIMITY_REPORT_SIZE) {
+               /* Invalid report data length of proximity packet. */
+               dev_err(dev, "invalid proximity data, length=%d\n", report_len);
+               return 0;
+       }
+
        if (report_id == PIP_TOUCH_REPORT_ID)
                cyapa_pip_report_touches(cyapa, &report_data);
+       else if (report_id == PIP_PROXIMITY_REPORT_ID)
+               cyapa_pip_report_proximity(cyapa, &report_data);
        else
                cyapa_pip_report_buttons(cyapa, &report_data);
 
@@ -2771,4 +2828,6 @@ const struct cyapa_dev_ops cyapa_gen5_ops = {
        .irq_cmd_handler = cyapa_pip_irq_cmd_handler,
        .sort_empty_output_data = cyapa_empty_pip_output_data,
        .set_power_mode = cyapa_gen5_set_power_mode,
+
+       .set_proximity = cyapa_pip_set_proximity,
 };
index 8a6aa73b53733cd2a116ef8fbbba0866e34256c2..61f04132eb65f467f95382e18c255d9b6812304c 100644 (file)
@@ -310,6 +310,17 @@ static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
        return 0;
 }
 
+static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
+{
+       int error;
+
+       cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
+       error = cyapa_pip_set_proximity(cyapa, enable);
+       cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
+
+       return error;
+}
+
 static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
 {
        u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
@@ -687,6 +698,12 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa)
                        dev_warn(dev, "%s: failed to set power active mode.\n",
                                __func__);
 
+               /* By default, the trackpad proximity function is enabled. */
+               error = cyapa_pip_set_proximity(cyapa, true);
+               if (error)
+                       dev_warn(dev, "%s: failed to enable proximity.\n",
+                               __func__);
+
                /* Get trackpad product information. */
                error = cyapa_gen6_read_sys_info(cyapa);
                if (error)
@@ -727,4 +744,6 @@ const struct cyapa_dev_ops cyapa_gen6_ops = {
        .irq_cmd_handler = cyapa_pip_irq_cmd_handler,
        .sort_empty_output_data = cyapa_empty_pip_output_data,
        .set_power_mode = cyapa_gen6_set_power_mode,
+
+       .set_proximity = cyapa_gen6_set_proximity,
 };