diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-02-20 10:05:39 +0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-02-20 10:05:39 +0400 |
commit | 2d9f0d964be94fd51c7303288c6f9c88bf2381fe (patch) | |
tree | 2631c3e82abc145a6e9c8e2c18687833c71de012 /drivers/input/mouse | |
parent | 9937c026820baabd1e908a9c1e6bdc846293000a (diff) | |
parent | 005a69d632cd8694061c2dd27492fe874780b5ee (diff) | |
download | linux-2d9f0d964be94fd51c7303288c6f9c88bf2381fe.tar.xz |
Merge branch 'next' into for-linus
Prepare first set of updates for 3.9 merge window.
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r-- | drivers/input/mouse/Kconfig | 22 | ||||
-rw-r--r-- | drivers/input/mouse/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/mouse/alps.c | 773 | ||||
-rw-r--r-- | drivers/input/mouse/alps.h | 145 | ||||
-rw-r--r-- | drivers/input/mouse/cyapa.c | 973 | ||||
-rw-r--r-- | drivers/input/mouse/cypress_ps2.c | 725 | ||||
-rw-r--r-- | drivers/input/mouse/cypress_ps2.h | 191 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 32 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse.h | 1 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.c | 32 |
10 files changed, 2528 insertions, 368 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index cd6268cf7cd5..802bd6a72d73 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS If unsure, say Y. +config MOUSE_PS2_CYPRESS + bool "Cypress PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Cypress PS/2 Trackpad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_LIFEBOOK bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT default y @@ -193,6 +203,18 @@ config MOUSE_BCM5974 To compile this driver as a module, choose M here: the module will be called bcm5974. +config MOUSE_CYAPA + tristate "Cypress APA I2C Trackpad support" + depends on I2C + help + This driver adds support for Cypress All Points Addressable (APA) + I2C Trackpads, including the ones used in 2012 Samsung Chromebooks. + + Say Y here if you have a Cypress APA I2C Trackpad. + + To compile this driver as a module, choose M here: the module will be + called cyapa. + config MOUSE_INPORT tristate "InPort/MS/ATIXL busmouse" depends on ISA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 46ba7556fd4f..c25efdb3f288 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o +obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o @@ -32,3 +33,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o +psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index e229fa3cad96..7b99fc7c9438 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -27,14 +27,11 @@ /* * Definitions for ALPS version 3 and 4 command mode protocol */ -#define ALPS_V3_X_MAX 2000 -#define ALPS_V3_Y_MAX 1400 - -#define ALPS_BITMAP_X_BITS 15 -#define ALPS_BITMAP_Y_BITS 11 - #define ALPS_CMD_NIBBLE_10 0x01f2 +#define ALPS_REG_BASE_RUSHMORE 0xc2c0 +#define ALPS_REG_BASE_PINNACLE 0x0000 + static const struct alps_nibble_commands alps_v3_nibble_commands[] = { { PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */ { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ @@ -109,11 +106,14 @@ static const struct alps_model_info alps_model_data[] = { { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ - { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, - { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, }; +static void alps_set_abs_params_st(struct alps_data *priv, + struct input_dev *dev1); +static void alps_set_abs_params_mt(struct alps_data *priv, + struct input_dev *dev1); + /* * XXX - this entry is suspicious. First byte has zero lower nibble, * which is what a normal mouse would report. Also, the value 0x0e @@ -122,10 +122,10 @@ static const struct alps_model_info alps_model_data[] = { /* Packet formats are described in Documentation/input/alps.txt */ -static bool alps_is_valid_first_byte(const struct alps_model_info *model, +static bool alps_is_valid_first_byte(struct alps_data *priv, unsigned char data) { - return (data & model->mask0) == model->byte0; + return (data & priv->mask0) == priv->byte0; } static void alps_report_buttons(struct psmouse *psmouse, @@ -158,14 +158,13 @@ static void alps_report_buttons(struct psmouse *psmouse, static void alps_process_packet_v1_v2(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; int x, y, z, ges, fin, left, right, middle; int back = 0, forward = 0; - if (model->proto_version == ALPS_PROTO_V1) { + if (priv->proto_version == ALPS_PROTO_V1) { left = packet[2] & 0x10; right = packet[2] & 0x08; middle = 0; @@ -181,12 +180,12 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) z = packet[5]; } - if (model->flags & ALPS_FW_BK_1) { + if (priv->flags & ALPS_FW_BK_1) { back = packet[0] & 0x10; forward = packet[2] & 4; } - if (model->flags & ALPS_FW_BK_2) { + if (priv->flags & ALPS_FW_BK_2) { back = packet[3] & 4; forward = packet[2] & 4; if ((middle = forward && back)) @@ -196,7 +195,7 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) ges = packet[2] & 1; fin = packet[2] & 2; - if ((model->flags & ALPS_DUALPOINT) && z == 127) { + if ((priv->flags & ALPS_DUALPOINT) && z == 127) { input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); @@ -239,15 +238,15 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - if (model->flags & ALPS_WHEEL) + if (priv->flags & ALPS_WHEEL) input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); - if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { input_report_key(dev, BTN_FORWARD, forward); input_report_key(dev, BTN_BACK, back); } - if (model->flags & ALPS_FOUR_BUTTONS) { + if (priv->flags & ALPS_FOUR_BUTTONS) { input_report_key(dev, BTN_0, packet[2] & 4); input_report_key(dev, BTN_1, packet[0] & 0x10); input_report_key(dev, BTN_2, packet[3] & 4); @@ -267,7 +266,8 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) * These points are returned in x1, y1, x2, and y2 when the return value * is greater than 0. */ -static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, +static int alps_process_bitmap(struct alps_data *priv, + unsigned int x_map, unsigned int y_map, int *x1, int *y1, int *x2, int *y2) { struct alps_bitmap_point { @@ -309,7 +309,7 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, * y bitmap is reversed for what we need (lower positions are in * higher bits), so we process from the top end. */ - y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - ALPS_BITMAP_Y_BITS); + y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits); prev_bit = 0; point = &y_low; for (i = 0; y_map != 0; i++, y_map <<= 1) { @@ -355,16 +355,18 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, } } - *x1 = (ALPS_V3_X_MAX * (2 * x_low.start_bit + x_low.num_bits - 1)) / - (2 * (ALPS_BITMAP_X_BITS - 1)); - *y1 = (ALPS_V3_Y_MAX * (2 * y_low.start_bit + y_low.num_bits - 1)) / - (2 * (ALPS_BITMAP_Y_BITS - 1)); + *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); if (fingers > 1) { - *x2 = (ALPS_V3_X_MAX * (2 * x_high.start_bit + x_high.num_bits - 1)) / - (2 * (ALPS_BITMAP_X_BITS - 1)); - *y2 = (ALPS_V3_Y_MAX * (2 * y_high.start_bit + y_high.num_bits - 1)) / - (2 * (ALPS_BITMAP_Y_BITS - 1)); + *x2 = (priv->x_max * + (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + *y2 = (priv->y_max * + (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); } return fingers; @@ -448,17 +450,57 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) return; } +static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) +{ + f->left = !!(p[3] & 0x01); + f->right = !!(p[3] & 0x02); + f->middle = !!(p[3] & 0x04); + + f->ts_left = !!(p[3] & 0x10); + f->ts_right = !!(p[3] & 0x20); + f->ts_middle = !!(p[3] & 0x40); +} + +static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p) +{ + f->first_mp = !!(p[4] & 0x40); + f->is_mp = !!(p[0] & 0x40); + + f->fingers = (p[5] & 0x3) + 1; + f->x_map = ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + + f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->z = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); +} + +static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p) +{ + alps_decode_pinnacle(f, p); + + f->x_map |= (p[5] & 0x10) << 11; + f->y_map |= (p[5] & 0x20) << 6; +} + static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; - int x, y, z; - int left, right, middle; int x1 = 0, y1 = 0, x2 = 0, y2 = 0; int fingers = 0, bmap_fingers; - unsigned int x_bitmap, y_bitmap; + struct alps_fields f; + + priv->decode_fields(&f, packet); /* * There's no single feature of touchpad position and bitmap packets @@ -473,16 +515,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) * packet. Check for this, and when it happens process the * position packet as usual. */ - if (packet[0] & 0x40) { - fingers = (packet[5] & 0x3) + 1; - x_bitmap = ((packet[4] & 0x7e) << 8) | - ((packet[1] & 0x7f) << 2) | - ((packet[0] & 0x30) >> 4); - y_bitmap = ((packet[3] & 0x70) << 4) | - ((packet[2] & 0x7f) << 1) | - (packet[4] & 0x01); - - bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap, + if (f.is_mp) { + fingers = f.fingers; + bmap_fingers = alps_process_bitmap(priv, + f.x_map, f.y_map, &x1, &y1, &x2, &y2); /* @@ -493,7 +529,7 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) fingers = bmap_fingers; /* Now process position packet */ - packet = priv->multi_data; + priv->decode_fields(&f, priv->multi_data); } else { priv->multi_packet = 0; } @@ -507,10 +543,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) * out misidentified bitmap packets, we reject anything with this * bit set. */ - if (packet[0] & 0x40) + if (f.is_mp) return; - if (!priv->multi_packet && (packet[4] & 0x40)) { + if (!priv->multi_packet && f.first_mp) { priv->multi_packet = 1; memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); return; @@ -518,22 +554,13 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) priv->multi_packet = 0; - left = packet[3] & 0x01; - right = packet[3] & 0x02; - middle = packet[3] & 0x04; - - x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) | - ((packet[0] & 0x30) >> 4); - y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f); - z = packet[5] & 0x7f; - /* * Sometimes the hardware sends a single packet with z = 0 * in the middle of a stream. Real releases generate packets * with x, y, and z all zero, so these seem to be flukes. * Ignore them. */ - if (x && y && !z) + if (f.x && f.y && !f.z) return; /* @@ -541,12 +568,12 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) * to rely on ST data. */ if (!fingers) { - x1 = x; - y1 = y; - fingers = z > 0 ? 1 : 0; + x1 = f.x; + y1 = f.y; + fingers = f.z > 0 ? 1 : 0; } - if (z >= 64) + if (f.z >= 64) input_report_key(dev, BTN_TOUCH, 1); else input_report_key(dev, BTN_TOUCH, 0); @@ -555,26 +582,22 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) input_mt_report_finger_count(dev, fingers); - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); - input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_LEFT, f.left); + input_report_key(dev, BTN_RIGHT, f.right); + input_report_key(dev, BTN_MIDDLE, f.middle); - if (z > 0) { - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + if (f.z > 0) { + input_report_abs(dev, ABS_X, f.x); + input_report_abs(dev, ABS_Y, f.y); } - input_report_abs(dev, ABS_PRESSURE, z); + input_report_abs(dev, ABS_PRESSURE, f.z); input_sync(dev); if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { - left = packet[3] & 0x10; - right = packet[3] & 0x20; - middle = packet[3] & 0x40; - - input_report_key(dev2, BTN_LEFT, left); - input_report_key(dev2, BTN_RIGHT, right); - input_report_key(dev2, BTN_MIDDLE, middle); + input_report_key(dev2, BTN_LEFT, f.ts_left); + input_report_key(dev2, BTN_RIGHT, f.ts_right); + input_report_key(dev2, BTN_MIDDLE, f.ts_middle); input_sync(dev2); } } @@ -639,7 +662,7 @@ static void alps_process_packet_v4(struct psmouse *psmouse) ((priv->multi_data[3] & 0x1f) << 5) | (priv->multi_data[1] & 0x1f); - fingers = alps_process_bitmap(x_bitmap, y_bitmap, + fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap, &x1, &y1, &x2, &y2); /* Store MT data.*/ @@ -696,25 +719,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse) input_sync(dev); } -static void alps_process_packet(struct psmouse *psmouse) -{ - struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; - - switch (model->proto_version) { - case ALPS_PROTO_V1: - case ALPS_PROTO_V2: - alps_process_packet_v1_v2(psmouse); - break; - case ALPS_PROTO_V3: - alps_process_packet_v3(psmouse); - break; - case ALPS_PROTO_V4: - alps_process_packet_v4(psmouse); - break; - } -} - static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) @@ -765,14 +769,14 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) if (((psmouse->packet[3] | psmouse->packet[4] | psmouse->packet[5]) & 0x80) || - (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) { + (!alps_is_valid_first_byte(priv, psmouse->packet[6]))) { psmouse_dbg(psmouse, "refusing packet %4ph (suspected interleaved ps/2)\n", psmouse->packet + 3); return PSMOUSE_BAD_DATA; } - alps_process_packet(psmouse); + priv->process_packet(psmouse); /* Continue with the next packet */ psmouse->packet[0] = psmouse->packet[6]; @@ -816,6 +820,7 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) static void alps_flush_packet(unsigned long data) { struct psmouse *psmouse = (struct psmouse *)data; + struct alps_data *priv = psmouse->private; serio_pause_rx(psmouse->ps2dev.serio); @@ -833,7 +838,7 @@ static void alps_flush_packet(unsigned long data) "refusing packet %3ph (suspected interleaved ps/2)\n", psmouse->packet + 3); } else { - alps_process_packet(psmouse); + priv->process_packet(psmouse); } psmouse->pktcnt = 0; } @@ -844,7 +849,6 @@ static void alps_flush_packet(unsigned long data) static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ if (psmouse->pktcnt == 3) { @@ -857,15 +861,15 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ - if ((model->flags & ALPS_PS2_INTERLEAVED) && + if ((priv->flags & ALPS_PS2_INTERLEAVED) && psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) { return alps_handle_interleaved_ps2(psmouse); } - if (!alps_is_valid_first_byte(model, psmouse->packet[0])) { + if (!alps_is_valid_first_byte(priv, psmouse->packet[0])) { psmouse_dbg(psmouse, "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", - psmouse->packet[0], model->mask0, model->byte0); + psmouse->packet[0], priv->mask0, priv->byte0); return PSMOUSE_BAD_DATA; } @@ -879,7 +883,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) } if (psmouse->pktcnt == psmouse->pktsize) { - alps_process_packet(psmouse); + priv->process_packet(psmouse); return PSMOUSE_FULL_PACKET; } @@ -967,24 +971,42 @@ static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, return __alps_command_mode_write_reg(psmouse, value); } +static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, + int repeated_command, unsigned char *param) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + param[0] = 0; + if (init_command && ps2_command(ps2dev, param, init_command)) + return -EIO; + + if (ps2_command(ps2dev, NULL, repeated_command) || + ps2_command(ps2dev, NULL, repeated_command) || + ps2_command(ps2dev, NULL, repeated_command)) + return -EIO; + + param[0] = param[1] = param[2] = 0xff; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -EIO; + + psmouse_dbg(psmouse, "%2.2X report: %2.2x %2.2x %2.2x\n", + repeated_command, param[0], param[1], param[2]); + return 0; +} + static int alps_enter_command_mode(struct psmouse *psmouse, unsigned char *resp) { unsigned char param[4]; - struct ps2dev *ps2dev = &psmouse->ps2dev; - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_RESET_WRAP, param)) { psmouse_err(psmouse, "failed to enter command mode\n"); return -1; } - if (param[0] != 0x88 && param[1] != 0x07) { + if (param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) { psmouse_dbg(psmouse, - "unknown response while entering command mode: %2.2x %2.2x %2.2x\n", - param[0], param[1], param[2]); + "unknown response while entering command mode\n"); return -1; } @@ -1001,99 +1023,6 @@ static inline int alps_exit_command_mode(struct psmouse *psmouse) return 0; } -static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; - unsigned char param[4]; - const struct alps_model_info *model = NULL; - int i; - - /* - * First try "E6 report". - * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. - * The bits 0-2 of the first byte will be 1s if some buttons are - * pressed. - */ - param[0] = 0; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) - return NULL; - - param[0] = param[1] = param[2] = 0xff; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) - return NULL; - - psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - - if ((param[0] & 0xf8) != 0 || param[1] != 0 || - (param[2] != 10 && param[2] != 100)) - return NULL; - - /* - * Now try "E7 report". Allowed responses are in - * alps_model_data[].signature - */ - param[0] = 0; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) - return NULL; - - param[0] = param[1] = param[2] = 0xff; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) - return NULL; - - psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - - if (version) { - for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) - /* empty */; - *version = (param[0] << 8) | (param[1] << 4) | i; - } - - for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { - if (!memcmp(param, alps_model_data[i].signature, - sizeof(alps_model_data[i].signature))) { - model = alps_model_data + i; - break; - } - } - - if (model && model->proto_version > ALPS_PROTO_V2) { - /* - * Need to check command mode response to identify - * model - */ - model = NULL; - if (alps_enter_command_mode(psmouse, param)) { - psmouse_warn(psmouse, - "touchpad failed to enter command mode\n"); - } else { - for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { - if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && - alps_model_data[i].command_mode_resp == param[0]) { - model = alps_model_data + i; - break; - } - } - alps_exit_command_mode(psmouse); - - if (!model) - psmouse_dbg(psmouse, - "Unknown command mode response %2.2x\n", - param[0]); - } - } - - return model; -} - /* * For DualPoint devices select the device that should respond to * subsequent commands. It looks like glidepad is behind stickpointer, @@ -1137,18 +1066,10 @@ static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) static int alps_get_status(struct psmouse *psmouse, char *param) { - struct ps2dev *ps2dev = &psmouse->ps2dev; - /* Get status: 0xF5 0xF5 0xF5 0xE9 */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_DISABLE, param)) return -1; - psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - return 0; } @@ -1190,16 +1111,16 @@ static int alps_poll(struct psmouse *psmouse) unsigned char buf[sizeof(psmouse->packet)]; bool poll_failed; - if (priv->i->flags & ALPS_PASS) + if (priv->flags & ALPS_PASS) alps_passthrough_mode_v2(psmouse, true); poll_failed = ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; - if (priv->i->flags & ALPS_PASS) + if (priv->flags & ALPS_PASS) alps_passthrough_mode_v2(psmouse, false); - if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) + if (poll_failed || (buf[0] & priv->mask0) != priv->byte0) return -1; if ((psmouse->badbyte & 0xc8) == 0x08) { @@ -1217,9 +1138,8 @@ static int alps_poll(struct psmouse *psmouse) static int alps_hw_init_v1_v2(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; - if ((model->flags & ALPS_PASS) && + if ((priv->flags & ALPS_PASS) && alps_passthrough_mode_v2(psmouse, true)) { return -1; } @@ -1234,7 +1154,7 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse) return -1; } - if ((model->flags & ALPS_PASS) && + if ((priv->flags & ALPS_PASS) && alps_passthrough_mode_v2(psmouse, false)) { return -1; } @@ -1249,26 +1169,31 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse) } /* - * Enable or disable passthrough mode to the trackstick. Must be in - * command mode when calling this function. + * Enable or disable passthrough mode to the trackstick. */ -static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable) +static int alps_passthrough_mode_v3(struct psmouse *psmouse, + int reg_base, bool enable) { - int reg_val; + int reg_val, ret = -1; - reg_val = alps_command_mode_read_reg(psmouse, 0x0008); - if (reg_val == -1) + if (alps_enter_command_mode(psmouse, NULL)) return -1; + reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008); + if (reg_val == -1) + goto error; + if (enable) reg_val |= 0x01; else reg_val &= ~0x01; - if (__alps_command_mode_write_reg(psmouse, reg_val)) - return -1; + ret = __alps_command_mode_write_reg(psmouse, reg_val); - return 0; +error: + if (alps_exit_command_mode(psmouse)) + ret = -1; + return ret; } /* Must be in command mode when calling this function */ @@ -1287,73 +1212,102 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse) return 0; } -static int alps_hw_init_v3(struct psmouse *psmouse) +static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base) { - struct alps_data *priv = psmouse->private; - struct ps2dev *ps2dev = &psmouse->ps2dev; - int reg_val; - unsigned char param[4]; - - priv->nibble_commands = alps_v3_nibble_commands; - priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + int ret = -EIO, reg_val; if (alps_enter_command_mode(psmouse, NULL)) goto error; - /* Check for trackstick */ - reg_val = alps_command_mode_read_reg(psmouse, 0x0008); + reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08); if (reg_val == -1) goto error; - if (reg_val & 0x80) { - if (alps_passthrough_mode_v3(psmouse, true)) - goto error; - if (alps_exit_command_mode(psmouse)) - goto error; + + /* bit 7: trackstick is present */ + ret = reg_val & 0x80 ? 0 : -ENODEV; + +error: + alps_exit_command_mode(psmouse); + return ret; +} + +static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int ret = 0; + unsigned char param[4]; + + if (alps_passthrough_mode_v3(psmouse, reg_base, true)) + return -EIO; + + /* + * E7 report for the trackstick + * + * There have been reports of failures to seem to trace back + * to the above trackstick check failing. When these occur + * this E7 report fails, so when that happens we continue + * with the assumption that there isn't a trackstick after + * all. + */ + if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) { + psmouse_warn(psmouse, "trackstick E7 report failed\n"); + ret = -ENODEV; + } else { + psmouse_dbg(psmouse, + "trackstick E7 report: %2.2x %2.2x %2.2x\n", + param[0], param[1], param[2]); /* - * E7 report for the trackstick - * - * There have been reports of failures to seem to trace back - * to the above trackstick check failing. When these occur - * this E7 report fails, so when that happens we continue - * with the assumption that there isn't a trackstick after - * all. + * Not sure what this does, but it is absolutely + * essential. Without it, the touchpad does not + * work at all and the trackstick just emits normal + * PS/2 packets. */ - param[0] = 0x64; - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { - psmouse_warn(psmouse, "trackstick E7 report failed\n"); - } else { - psmouse_dbg(psmouse, - "trackstick E7 report: %2.2x %2.2x %2.2x\n", - param[0], param[1], param[2]); - - /* - * Not sure what this does, but it is absolutely - * essential. Without it, the touchpad does not - * work at all and the trackstick just emits normal - * PS/2 packets. - */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - alps_command_mode_send_nibble(psmouse, 0x9) || - alps_command_mode_send_nibble(psmouse, 0x4)) { - psmouse_err(psmouse, - "Error sending magic E6 sequence\n"); - goto error_passthrough; - } + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + alps_command_mode_send_nibble(psmouse, 0x9) || + alps_command_mode_send_nibble(psmouse, 0x4)) { + psmouse_err(psmouse, + "Error sending magic E6 sequence\n"); + ret = -EIO; + goto error; } - if (alps_enter_command_mode(psmouse, NULL)) - goto error_passthrough; - if (alps_passthrough_mode_v3(psmouse, false)) - goto error; + /* + * This ensures the trackstick packets are in the format + * supported by this driver. If bit 1 isn't set the packet + * format is different. + */ + if (alps_enter_command_mode(psmouse, NULL) || + alps_command_mode_write_reg(psmouse, + reg_base + 0x08, 0x82) || + alps_exit_command_mode(psmouse)) + ret = -EIO; } - if (alps_absolute_mode_v3(psmouse)) { +error: + if (alps_passthrough_mode_v3(psmouse, reg_base, false)) + ret = -EIO; + + return ret; +} + +static int alps_hw_init_v3(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int reg_val; + unsigned char param[4]; + + reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); + if (reg_val == -EIO) + goto error; + if (reg_val == 0 && + alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) + goto error; + + if (alps_enter_command_mode(psmouse, NULL) || + alps_absolute_mode_v3(psmouse)) { psmouse_err(psmouse, "Failed to enter absolute mode\n"); goto error; } @@ -1390,14 +1344,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse) if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) goto error; - /* - * This ensures the trackstick packets are in the format - * supported by this driver. If bit 1 isn't set the packet - * format is different. - */ - if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82)) - goto error; - alps_exit_command_mode(psmouse); /* Set rate and enable data reporting */ @@ -1410,10 +1356,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse) return 0; -error_passthrough: - /* Something failed while in passthrough mode, so try to get out */ - if (!alps_enter_command_mode(psmouse, NULL)) - alps_passthrough_mode_v3(psmouse, false); error: /* * Leaving the touchpad in command mode will essentially render @@ -1424,6 +1366,50 @@ error: return -1; } +static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + struct ps2dev *ps2dev = &psmouse->ps2dev; + int reg_val, ret = -1; + + if (priv->flags & ALPS_DUALPOINT) { + reg_val = alps_setup_trackstick_v3(psmouse, + ALPS_REG_BASE_RUSHMORE); + if (reg_val == -EIO) + goto error; + if (reg_val == -ENODEV) + priv->flags &= ~ALPS_DUALPOINT; + } + + if (alps_enter_command_mode(psmouse, NULL) || + alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 || + alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00)) + goto error; + + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val & 0xfd)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64)) + goto error; + + /* enter absolute mode */ + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02)) + goto error; + + alps_exit_command_mode(psmouse); + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + +error: + alps_exit_command_mode(psmouse); + return ret; +} + /* Must be in command mode when calling this function */ static int alps_absolute_mode_v4(struct psmouse *psmouse) { @@ -1442,13 +1428,9 @@ static int alps_absolute_mode_v4(struct psmouse *psmouse) static int alps_hw_init_v4(struct psmouse *psmouse) { - struct alps_data *priv = psmouse->private; struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; - priv->nibble_commands = alps_v4_nibble_commands; - priv->addr_command = PSMOUSE_CMD_DISABLE; - if (alps_enter_command_mode(psmouse, NULL)) goto error; @@ -1517,39 +1499,140 @@ error: return -1; } -static int alps_hw_init(struct psmouse *psmouse) +static void alps_set_defaults(struct alps_data *priv) { - struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; - int ret = -1; + priv->byte0 = 0x8f; + priv->mask0 = 0x8f; + priv->flags = ALPS_DUALPOINT; + + priv->x_max = 2000; + priv->y_max = 1400; + priv->x_bits = 15; + priv->y_bits = 11; - switch (model->proto_version) { + switch (priv->proto_version) { case ALPS_PROTO_V1: case ALPS_PROTO_V2: - ret = alps_hw_init_v1_v2(psmouse); + priv->hw_init = alps_hw_init_v1_v2; + priv->process_packet = alps_process_packet_v1_v2; + priv->set_abs_params = alps_set_abs_params_st; break; case ALPS_PROTO_V3: - ret = alps_hw_init_v3(psmouse); + priv->hw_init = alps_hw_init_v3; + priv->process_packet = alps_process_packet_v3; + priv->set_abs_params = alps_set_abs_params_mt; + priv->decode_fields = alps_decode_pinnacle; + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; break; case ALPS_PROTO_V4: - ret = alps_hw_init_v4(psmouse); + priv->hw_init = alps_hw_init_v4; + priv->process_packet = alps_process_packet_v4; + priv->set_abs_params = alps_set_abs_params_mt; + priv->nibble_commands = alps_v4_nibble_commands; + priv->addr_command = PSMOUSE_CMD_DISABLE; break; } +} - return ret; +static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv, + unsigned char *e7, unsigned char *ec) +{ + const struct alps_model_info *model; + int i; + + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + model = &alps_model_data[i]; + + if (!memcmp(e7, model->signature, sizeof(model->signature)) && + (!model->command_mode_resp || + model->command_mode_resp == ec[2])) { + + priv->proto_version = model->proto_version; + alps_set_defaults(priv); + + priv->flags = model->flags; + priv->byte0 = model->byte0; + priv->mask0 = model->mask0; + + return 0; + } + } + + return -EINVAL; +} + +static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) +{ + unsigned char e6[4], e7[4], ec[4]; + + /* + * First try "E6 report". + * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. + * The bits 0-2 of the first byte will be 1s if some buttons are + * pressed. + */ + if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETSCALE11, e6)) + return -EIO; + + if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10 && e6[2] != 100)) + return -EINVAL; + + /* + * Now get the "E7" and "EC" reports. These will uniquely identify + * most ALPS touchpads. + */ + if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETSCALE21, e7) || + alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_RESET_WRAP, ec) || + alps_exit_command_mode(psmouse)) + return -EIO; + + if (alps_match_table(psmouse, priv, e7, ec) == 0) { + return 0; + } else if (ec[0] == 0x88 && ec[1] == 0x08) { + priv->proto_version = ALPS_PROTO_V3; + alps_set_defaults(priv); + + priv->hw_init = alps_hw_init_rushmore_v3; + priv->decode_fields = alps_decode_rushmore; + priv->x_bits = 16; + priv->y_bits = 12; + + /* hack to make addr_command, nibble_command available */ + psmouse->private = priv; + + if (alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_RUSHMORE)) + priv->flags &= ~ALPS_DUALPOINT; + + return 0; + } else if (ec[0] == 0x88 && ec[1] == 0x07 && + ec[2] >= 0x90 && ec[2] <= 0x9d) { + priv->proto_version = ALPS_PROTO_V3; + alps_set_defaults(priv); + + return 0; + } + + psmouse_info(psmouse, + "Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n", + e7[0], e7[1], e7[2], ec[0], ec[1], ec[2]); + + return -EINVAL; } static int alps_reconnect(struct psmouse *psmouse) { - const struct alps_model_info *model; + struct alps_data *priv = psmouse->private; psmouse_reset(psmouse); - model = alps_get_model(psmouse, NULL); - if (!model) + if (alps_identify(psmouse, priv) < 0) return -1; - return alps_hw_init(psmouse); + return priv->hw_init(psmouse); } static void alps_disconnect(struct psmouse *psmouse) @@ -1562,12 +1645,33 @@ static void alps_disconnect(struct psmouse *psmouse) kfree(priv); } +static void alps_set_abs_params_st(struct alps_data *priv, + struct input_dev *dev1) +{ + input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); +} + +static void alps_set_abs_params_mt(struct alps_data *priv, + struct input_dev *dev1) +{ + set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); + input_mt_init_slots(dev1, 2, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); + + set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); + set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); + set_bit(BTN_TOOL_QUADTAP, dev1->keybit); + + input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); +} + int alps_init(struct psmouse *psmouse) { struct alps_data *priv; - const struct alps_model_info *model; struct input_dev *dev1 = psmouse->dev, *dev2; - int version; priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); dev2 = input_allocate_device(); @@ -1581,13 +1685,10 @@ int alps_init(struct psmouse *psmouse) psmouse_reset(psmouse); - model = alps_get_model(psmouse, &version); - if (!model) + if (alps_identify(psmouse, priv) < 0) goto init_fail; - priv->i = model; - - if (alps_hw_init(psmouse)) + if (priv->hw_init(psmouse)) goto init_fail; /* @@ -1609,41 +1710,20 @@ int alps_init(struct psmouse *psmouse) dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); - switch (model->proto_version) { - case ALPS_PROTO_V1: - case ALPS_PROTO_V2: - input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); - break; - case ALPS_PROTO_V3: - case ALPS_PROTO_V4: - set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); - input_mt_init_slots(dev1, 2, 0); - input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); - input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); - - set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); - set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); - set_bit(BTN_TOOL_QUADTAP, dev1->keybit); - - input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); - break; - } - + priv->set_abs_params(priv, dev1); input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); - if (model->flags & ALPS_WHEEL) { + if (priv->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); } - if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); } - if (model->flags & ALPS_FOUR_BUTTONS) { + if (priv->flags & ALPS_FOUR_BUTTONS) { dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); @@ -1654,7 +1734,8 @@ int alps_init(struct psmouse *psmouse) snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); dev2->phys = priv->phys; - dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; + dev2->name = (priv->flags & ALPS_DUALPOINT) ? + "DualPoint Stick" : "PS/2 Mouse"; dev2->id.bustype = BUS_I8042; dev2->id.vendor = 0x0002; dev2->id.product = PSMOUSE_ALPS; @@ -1673,7 +1754,7 @@ int alps_init(struct psmouse *psmouse) psmouse->poll = alps_poll; psmouse->disconnect = alps_disconnect; psmouse->reconnect = alps_reconnect; - psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6; + psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6; /* We are having trouble resyncing ALPS touchpads so disable it for now */ psmouse->resync_time = 0; @@ -1690,18 +1771,16 @@ init_fail: int alps_detect(struct psmouse *psmouse, bool set_properties) { - int version; - const struct alps_model_info *model; + struct alps_data dummy; - model = alps_get_model(psmouse, &version); - if (!model) + if (alps_identify(psmouse, &dummy) < 0) return -1; if (set_properties) { psmouse->vendor = "ALPS"; - psmouse->name = model->flags & ALPS_DUALPOINT ? + psmouse->name = dummy.flags & ALPS_DUALPOINT ? "DualPoint TouchPad" : "GlidePoint"; - psmouse->model = version; + psmouse->model = dummy.proto_version << 8; } return 0; } diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index ae1ac354c778..970480551b6e 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -12,35 +12,146 @@ #ifndef _ALPS_H #define _ALPS_H -#define ALPS_PROTO_V1 0 -#define ALPS_PROTO_V2 1 -#define ALPS_PROTO_V3 2 -#define ALPS_PROTO_V4 3 +#define ALPS_PROTO_V1 1 +#define ALPS_PROTO_V2 2 +#define ALPS_PROTO_V3 3 +#define ALPS_PROTO_V4 4 +/** + * struct alps_model_info - touchpad ID table + * @signature: E7 response string to match. + * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response + * (aka command mode response) identifies the firmware minor version. This + * can be used to distinguish different hardware models which are not + * uniquely identifiable through their E7 responses. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * + * Many (but not all) ALPS touchpads can be identified by looking at the + * values returned in the "E7 report" and/or the "EC report." This table + * lists a number of such touchpads. + */ struct alps_model_info { - unsigned char signature[3]; - unsigned char command_mode_resp; /* v3/v4 only */ + unsigned char signature[3]; + unsigned char command_mode_resp; unsigned char proto_version; - unsigned char byte0, mask0; - unsigned char flags; + unsigned char byte0, mask0; + unsigned char flags; }; +/** + * struct alps_nibble_commands - encodings for register accesses + * @command: PS/2 command used for the nibble + * @data: Data supplied as an argument to the PS/2 command, if applicable + * + * The ALPS protocol uses magic sequences to transmit binary data to the + * touchpad, as it is generally not OK to send arbitrary bytes out the + * PS/2 port. Each of the sequences in this table sends one nibble of the + * register address or (write) data. Different versions of the ALPS protocol + * use slightly different encodings. + */ struct alps_nibble_commands { int command; unsigned char data; }; +/** + * struct alps_fields - decoded version of the report packet + * @x_map: Bitmap of active X positions for MT. + * @y_map: Bitmap of active Y positions for MT. + * @fingers: Number of fingers for MT. + * @x: X position for ST. + * @y: Y position for ST. + * @z: Z position for ST. + * @first_mp: Packet is the first of a multi-packet report. + * @is_mp: Packet is part of a multi-packet report. + * @left: Left touchpad button is active. + * @right: Right touchpad button is active. + * @middle: Middle touchpad button is active. + * @ts_left: Left trackstick button is active. + * @ts_right: Right trackstick button is active. + * @ts_middle: Middle trackstick button is active. + */ +struct alps_fields { + unsigned int x_map; + unsigned int y_map; + unsigned int fingers; + unsigned int x; + unsigned int y; + unsigned int z; + unsigned int first_mp:1; + unsigned int is_mp:1; + + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + + unsigned int ts_left:1; + unsigned int ts_right:1; + unsigned int ts_middle:1; +}; + +/** + * struct alps_data - private data structure for the ALPS driver + * @dev2: "Relative" device used to report trackstick or mouse activity. + * @phys: Physical path for the relative device. + * @nibble_commands: Command mapping used for touchpad register accesses. + * @addr_command: Command used to tell the touchpad that a register address + * follows. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * @x_max: Largest possible X position value. + * @y_max: Largest possible Y position value. + * @x_bits: Number of X bits in the MT bitmap. + * @y_bits: Number of Y bits in the MT bitmap. + * @hw_init: Protocol-specific hardware init function. + * @process_packet: Protocol-specific function to process a report packet. + * @decode_fields: Protocol-specific function to read packet bitfields. + * @set_abs_params: Protocol-specific function to configure the input_dev. + * @prev_fin: Finger bit from previous packet. + * @multi_packet: Multi-packet data in progress. + * @multi_data: Saved multi-packet data. + * @x1: First X coordinate from last MT report. + * @x2: Second X coordinate from last MT report. + * @y1: First Y coordinate from last MT report. + * @y2: Second Y coordinate from last MT report. + * @fingers: Number of fingers from last MT report. + * @quirks: Bitmap of ALPS_QUIRK_*. + * @timer: Timer for flushing out the final report packet in the stream. + */ struct alps_data { - struct input_dev *dev2; /* Relative device */ - char phys[32]; /* Phys */ - const struct alps_model_info *i;/* Info */ + struct input_dev *dev2; + char phys[32]; + + /* these are autodetected when the device is identified */ const struct alps_nibble_commands *nibble_commands; - int addr_command; /* Command to set register address */ - int prev_fin; /* Finger bit from previous packet */ - int multi_packet; /* Multi-packet data in progress */ - unsigned char multi_data[6]; /* Saved multi-packet data */ - int x1, x2, y1, y2; /* Coordinates from last MT report */ - int fingers; /* Number of fingers from MT report */ + int addr_command; + unsigned char proto_version; + unsigned char byte0, mask0; + unsigned char flags; + int x_max; + int y_max; + int x_bits; + int y_bits; + + int (*hw_init)(struct psmouse *psmouse); + void (*process_packet)(struct psmouse *psmouse); + void (*decode_fields)(struct alps_fields *f, unsigned char *p); + void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1); + + int prev_fin; + int multi_packet; + unsigned char multi_data[6]; + int x1, x2, y1, y2; + int fingers; u8 quirks; struct timer_list timer; }; diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c new file mode 100644 index 000000000000..b409c3d7d4fb --- /dev/null +++ b/drivers/input/mouse/cyapa.c @@ -0,0 +1,973 @@ +/* + * Cypress APA trackpad with I2C interface + * + * Author: Dudley Du <dudl@cypress.com> + * Further cleanup and restructuring by: + * Daniel Kurtz <djkurtz@chromium.org> + * Benson Leung <bleung@chromium.org> + * + * Copyright (C) 2011-2012 Cypress Semiconductor, Inc. + * Copyright (C) 2011-2012 Google, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> + +/* APA trackpad firmware generation */ +#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */ + +#define CYAPA_NAME "Cypress APA Trackpad (cyapa)" + +/* commands for read/write registers of Cypress trackpad */ +#define CYAPA_CMD_SOFT_RESET 0x00 +#define CYAPA_CMD_POWER_MODE 0x01 +#define CYAPA_CMD_DEV_STATUS 0x02 +#define CYAPA_CMD_GROUP_DATA 0x03 +#define CYAPA_CMD_GROUP_CMD 0x04 +#define CYAPA_CMD_GROUP_QUERY 0x05 +#define CYAPA_CMD_BL_STATUS 0x06 +#define CYAPA_CMD_BL_HEAD 0x07 +#define CYAPA_CMD_BL_CMD 0x08 +#define CYAPA_CMD_BL_DATA 0x09 +#define CYAPA_CMD_BL_ALL 0x0a +#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b +#define CYAPA_CMD_BLK_HEAD 0x0c + +/* report data start reg offset address. */ +#define DATA_REG_START_OFFSET 0x0000 + +#define BL_HEAD_OFFSET 0x00 +#define BL_DATA_OFFSET 0x10 + +/* + * Operational Device Status Register + * + * bit 7: Valid interrupt source + * bit 6 - 4: Reserved + * bit 3 - 2: Power status + * bit 1 - 0: Device status + */ +#define REG_OP_STATUS 0x00 +#define OP_STATUS_SRC 0x80 +#define OP_STATUS_POWER 0x0c +#define OP_STATUS_DEV 0x03 +#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV) + +/* + * Operational Finger Count/Button Flags Register + * + * bit 7 - 4: Number of touched finger + * bit 3: Valid data + * bit 2: Middle Physical Button + * bit 1: Right Physical Button + * bit 0: Left physical Button + */ +#define REG_OP_DATA1 0x01 +#define OP_DATA_VALID 0x08 +#define OP_DATA_MIDDLE_BTN 0x04 +#define OP_DATA_RIGHT_BTN 0x02 +#define OP_DATA_LEFT_BTN 0x01 +#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \ + OP_DATA_LEFT_BTN) + +/* + * Bootloader Status Register + * + * bit 7: Busy + * bit 6 - 5: Reserved + * bit 4: Bootloader running + * bit 3 - 1: Reserved + * bit 0: Checksum valid + */ +#define REG_BL_STATUS 0x01 +#define BL_STATUS_BUSY 0x80 +#define BL_STATUS_RUNNING 0x10 +#define BL_STATUS_DATA_VALID 0x08 +#define BL_STATUS_CSUM_VALID 0x01 + +/* + * Bootloader Error Register + * + * bit 7: Invalid + * bit 6: Invalid security key + * bit 5: Bootloading + * bit 4: Command checksum + * bit 3: Flash protection error + * bit 2: Flash checksum error + * bit 1 - 0: Reserved + */ +#define REG_BL_ERROR 0x02 +#define BL_ERROR_INVALID 0x80 +#define BL_ERROR_INVALID_KEY 0x40 +#define BL_ERROR_BOOTLOADING 0x20 +#define BL_ERROR_CMD_CSUM 0x10 +#define BL_ERROR_FLASH_PROT 0x08 +#define BL_ERROR_FLASH_CSUM 0x04 + +#define BL_STATUS_SIZE 3 /* length of bootloader status registers */ +#define BLK_HEAD_BYTES 32 + +#define PRODUCT_ID_SIZE 16 +#define QUERY_DATA_SIZE 27 +#define REG_PROTOCOL_GEN_QUERY_OFFSET 20 + +#define REG_OFFSET_DATA_BASE 0x0000 +#define REG_OFFSET_COMMAND_BASE 0x0028 +#define REG_OFFSET_QUERY_BASE 0x002a + +#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3) +#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4) +#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5) +#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \ + CAPABILITY_RIGHT_BTN_MASK | \ + CAPABILITY_MIDDLE_BTN_MASK) + +#define CYAPA_OFFSET_SOFT_RESET REG_OFFSET_COMMAND_BASE + +#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1) + +#define PWR_MODE_MASK 0xfc +#define PWR_MODE_FULL_ACTIVE (0x3f << 2) +#define PWR_MODE_IDLE (0x05 << 2) /* default sleep time is 50 ms. */ +#define PWR_MODE_OFF (0x00 << 2) + +#define PWR_STATUS_MASK 0x0c +#define PWR_STATUS_ACTIVE (0x03 << 2) +#define PWR_STATUS_IDLE (0x02 << 2) +#define PWR_STATUS_OFF (0x00 << 2) + +/* + * CYAPA trackpad device states. + * Used in register 0x00, bit1-0, DeviceStatus field. + * Other values indicate device is in an abnormal state and must be reset. + */ +#define CYAPA_DEV_NORMAL 0x03 +#define CYAPA_DEV_BUSY 0x01 + +enum cyapa_state { + CYAPA_STATE_OP, + CYAPA_STATE_BL_IDLE, + CYAPA_STATE_BL_ACTIVE, + CYAPA_STATE_BL_BUSY, + CYAPA_STATE_NO_DEVICE, +}; + + +struct cyapa_touch { + /* + * high bits or x/y position value + * bit 7 - 4: high 4 bits of x position value + * bit 3 - 0: high 4 bits of y position value + */ + u8 xy_hi; + u8 x_lo; /* low 8 bits of x position value. */ + u8 y_lo; /* low 8 bits of y position value. */ + u8 pressure; + /* id range is 1 - 15. It is incremented with every new touch. */ + u8 id; +} __packed; + +/* The touch.id is used as the MT slot id, thus max MT slot is 15 */ +#define CYAPA_MAX_MT_SLOTS 15 + +struct cyapa_reg_data { + /* + * bit 0 - 1: device status + * bit 3 - 2: power mode + * bit 6 - 4: reserved + * bit 7: interrupt valid bit + */ + u8 device_status; + /* + * bit 7 - 4: number of fingers currently touching pad + * bit 3: valid data check bit + * bit 2: middle mechanism button state if exists + * bit 1: right mechanism button state if exists + * bit 0: left mechanism button state if exists + */ + u8 finger_btn; + /* CYAPA reports up to 5 touches per packet. */ + struct cyapa_touch touches[5]; +} __packed; + +/* The main device structure */ +struct cyapa { + enum cyapa_state state; + + struct i2c_client *client; + struct input_dev *input; + char phys[32]; /* device physical location */ + int irq; + bool irq_wake; /* irq wake is enabled */ + bool smbus; + + /* read from query data region. */ + char product_id[16]; + u8 btn_capability; + u8 gen; + int max_abs_x; + int max_abs_y; + int physical_size_x; + int physical_size_y; +}; + +static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07 }; +static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07 }; + +struct cyapa_cmd_len { + u8 cmd; + u8 len; +}; + +#define CYAPA_ADAPTER_FUNC_NONE 0 +#define CYAPA_ADAPTER_FUNC_I2C 1 +#define CYAPA_ADAPTER_FUNC_SMBUS 2 +#define CYAPA_ADAPTER_FUNC_BOTH 3 + +/* + * macros for SMBus communication + */ +#define SMBUS_READ 0x01 +#define SMBUS_WRITE 0x00 +#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1)) +#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01)) +#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80 +#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40 + + /* for byte read/write command */ +#define CMD_RESET 0 +#define CMD_POWER_MODE 1 +#define CMD_DEV_STATUS 2 +#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1) +#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET) +#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE) +#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS) + + /* for group registers read/write command */ +#define REG_GROUP_DATA 0 +#define REG_GROUP_CMD 2 +#define REG_GROUP_QUERY 3 +#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3)) +#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA) +#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD) +#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY) + + /* for register block read/write command */ +#define CMD_BL_STATUS 0 +#define CMD_BL_HEAD 1 +#define CMD_BL_CMD 2 +#define CMD_BL_DATA 3 +#define CMD_BL_ALL 4 +#define CMD_BLK_PRODUCT_ID 5 +#define CMD_BLK_HEAD 6 +#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1)) + +/* register block read/write command in bootloader mode */ +#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS) +#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD) +#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD) +#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA) +#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL) + +/* register block read/write command in operational mode */ +#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID) +#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD) + +static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { + { CYAPA_OFFSET_SOFT_RESET, 1 }, + { REG_OFFSET_COMMAND_BASE + 1, 1 }, + { REG_OFFSET_DATA_BASE, 1 }, + { REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) }, + { REG_OFFSET_COMMAND_BASE, 0 }, + { REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE }, + { BL_HEAD_OFFSET, 3 }, + { BL_HEAD_OFFSET, 16 }, + { BL_HEAD_OFFSET, 16 }, + { BL_DATA_OFFSET, 16 }, + { BL_HEAD_OFFSET, 32 }, + { REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE }, + { REG_OFFSET_DATA_BASE, 32 } +}; + +static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { + { CYAPA_SMBUS_RESET, 1 }, + { CYAPA_SMBUS_POWER_MODE, 1 }, + { CYAPA_SMBUS_DEV_STATUS, 1 }, + { CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) }, + { CYAPA_SMBUS_GROUP_CMD, 2 }, + { CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE }, + { CYAPA_SMBUS_BL_STATUS, 3 }, + { CYAPA_SMBUS_BL_HEAD, 16 }, + { CYAPA_SMBUS_BL_CMD, 16 }, + { CYAPA_SMBUS_BL_DATA, 16 }, + { CYAPA_SMBUS_BL_ALL, 32 }, + { CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE }, + { CYAPA_SMBUS_BLK_HEAD, 16 }, +}; + +static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, + u8 *values) +{ + return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values); +} + +static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg, + size_t len, const u8 *values) +{ + return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values); +} + +/* + * cyapa_smbus_read_block - perform smbus block read command + * @cyapa - private data structure of the driver + * @cmd - the properly encoded smbus command + * @len - expected length of smbus command result + * @values - buffer to store smbus command result + * + * Returns negative errno, else the number of bytes written. + * + * Note: + * In trackpad device, the memory block allocated for I2C register map + * is 256 bytes, so the max read block for I2C bus is 256 bytes. + */ +static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, + u8 *values) +{ + ssize_t ret; + u8 index; + u8 smbus_cmd; + u8 *buf; + struct i2c_client *client = cyapa->client; + + if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd)) + return -EINVAL; + + if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) { + /* read specific block registers command. */ + smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); + ret = i2c_smbus_read_block_data(client, smbus_cmd, values); + goto out; + } + + ret = 0; + for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) { + smbus_cmd = SMBUS_ENCODE_IDX(cmd, index); + smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ); + buf = values + I2C_SMBUS_BLOCK_MAX * index; + ret = i2c_smbus_read_block_data(client, smbus_cmd, buf); + if (ret < 0) + goto out; + } + +out: + return ret > 0 ? len : ret; +} + +static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx) +{ + u8 cmd; + + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + } + return i2c_smbus_read_byte_data(cyapa->client, cmd); +} + +static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value) +{ + u8 cmd; + + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + } + return i2c_smbus_write_byte_data(cyapa->client, cmd, value); +} + +static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values) +{ + u8 cmd; + size_t len; + + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + len = cyapa_smbus_cmds[cmd_idx].len; + return cyapa_smbus_read_block(cyapa, cmd, len, values); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + len = cyapa_i2c_cmds[cmd_idx].len; + return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); + } +} + +/* + * Query device for its current operating state. + * + */ +static int cyapa_get_state(struct cyapa *cyapa) +{ + int ret; + u8 status[BL_STATUS_SIZE]; + + cyapa->state = CYAPA_STATE_NO_DEVICE; + + /* + * Get trackpad status by reading 3 registers starting from 0. + * If the device is in the bootloader, this will be BL_HEAD. + * If the device is in operation mode, this will be the DATA regs. + * + */ + ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE, + status); + + /* + * On smbus systems in OP mode, the i2c_reg_read will fail with + * -ETIMEDOUT. In this case, try again using the smbus equivalent + * command. This should return a BL_HEAD indicating CYAPA_STATE_OP. + */ + if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO)) + ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status); + + if (ret != BL_STATUS_SIZE) + goto error; + + if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) { + switch (status[REG_OP_STATUS] & OP_STATUS_DEV) { + case CYAPA_DEV_NORMAL: + case CYAPA_DEV_BUSY: + cyapa->state = CYAPA_STATE_OP; + break; + default: + ret = -EAGAIN; + goto error; + } + } else { + if (status[REG_BL_STATUS] & BL_STATUS_BUSY) + cyapa->state = CYAPA_STATE_BL_BUSY; + else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING) + cyapa->state = CYAPA_STATE_BL_ACTIVE; + else + cyapa->state = CYAPA_STATE_BL_IDLE; + } + + return 0; +error: + return (ret < 0) ? ret : -EAGAIN; +} + +/* + * Poll device for its status in a loop, waiting up to timeout for a response. + * + * When the device switches state, it usually takes ~300 ms. + * However, when running a new firmware image, the device must calibrate its + * sensors, which can take as long as 2 seconds. + * + * Note: The timeout has granularity of the polling rate, which is 100 ms. + * + * Returns: + * 0 when the device eventually responds with a valid non-busy state. + * -ETIMEDOUT if device never responds (too many -EAGAIN) + * < 0 other errors + */ +static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout) +{ + int ret; + int tries = timeout / 100; + + ret = cyapa_get_state(cyapa); + while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) { + msleep(100); + ret = cyapa_get_state(cyapa); + } + return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret; +} + +static int cyapa_bl_deactivate(struct cyapa *cyapa) +{ + int ret; + + ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate), + bl_deactivate); + if (ret < 0) + return ret; + + /* wait for bootloader to switch to idle state; should take < 100ms */ + msleep(100); + ret = cyapa_poll_state(cyapa, 500); + if (ret < 0) + return ret; + if (cyapa->state != CYAPA_STATE_BL_IDLE) + return -EAGAIN; + return 0; +} + +/* + * Exit bootloader + * + * Send bl_exit command, then wait 50 - 100 ms to let device transition to + * operational mode. If this is the first time the device's firmware is + * running, it can take up to 2 seconds to calibrate its sensors. So, poll + * the device's new state for up to 2 seconds. + * + * Returns: + * -EIO failure while reading from device + * -EAGAIN device is stuck in bootloader, b/c it has invalid firmware + * 0 device is supported and in operational mode + */ +static int cyapa_bl_exit(struct cyapa *cyapa) +{ + int ret; + + ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit); + if (ret < 0) + return ret; + + /* + * Wait for bootloader to exit, and operation mode to start. + * Normally, this takes at least 50 ms. + */ + usleep_range(50000, 100000); + /* + * In addition, when a device boots for the first time after being + * updated to new firmware, it must first calibrate its sensors, which + * can take up to an additional 2 seconds. + */ + ret = cyapa_poll_state(cyapa, 2000); + if (ret < 0) + return ret; + if (cyapa->state != CYAPA_STATE_OP) + return -EAGAIN; + + return 0; +} + +/* + * Set device power mode + * + */ +static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode) +{ + struct device *dev = &cyapa->client->dev; + int ret; + u8 power; + + if (cyapa->state != CYAPA_STATE_OP) + return 0; + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE); + if (ret < 0) + return ret; + + power = ret & ~PWR_MODE_MASK; + power |= power_mode & PWR_MODE_MASK; + ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power); + if (ret < 0) + dev_err(dev, "failed to set power_mode 0x%02x err = %d\n", + power_mode, ret); + return ret; +} + +static int cyapa_get_query_data(struct cyapa *cyapa) +{ + u8 query_data[QUERY_DATA_SIZE]; + int ret; + + if (cyapa->state != CYAPA_STATE_OP) + return -EBUSY; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data); + if (ret < 0) + return ret; + if (ret != QUERY_DATA_SIZE) + return -EIO; + + memcpy(&cyapa->product_id[0], &query_data[0], 5); + cyapa->product_id[5] = '-'; + memcpy(&cyapa->product_id[6], &query_data[5], 6); + cyapa->product_id[12] = '-'; + memcpy(&cyapa->product_id[13], &query_data[11], 2); + cyapa->product_id[15] = '\0'; + + cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK; + + cyapa->gen = query_data[20] & 0x0f; + + cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22]; + cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23]; + + cyapa->physical_size_x = + ((query_data[24] & 0xf0) << 4) | query_data[25]; + cyapa->physical_size_y = + ((query_data[24] & 0x0f) << 8) | query_data[26]; + + return 0; +} + +/* + * Check if device is operational. + * + * An operational device is responding, has exited bootloader, and has + * firmware supported by this driver. + * + * Returns: + * -EBUSY no device or in bootloader + * -EIO failure while reading from device + * -EAGAIN device is still in bootloader + * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware + * -EINVAL device is in operational mode, but not supported by this driver + * 0 device is supported + */ +static int cyapa_check_is_operational(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + static const char unique_str[] = "CYTRA"; + int ret; + + ret = cyapa_poll_state(cyapa, 2000); + if (ret < 0) + return ret; + switch (cyapa->state) { + case CYAPA_STATE_BL_ACTIVE: + ret = cyapa_bl_deactivate(cyapa); + if (ret) + return ret; + + /* Fallthrough state */ + case CYAPA_STATE_BL_IDLE: + ret = cyapa_bl_exit(cyapa); + if (ret) + return ret; + + /* Fallthrough state */ + case CYAPA_STATE_OP: + ret = cyapa_get_query_data(cyapa); + if (ret < 0) + return ret; + + /* only support firmware protocol gen3 */ + if (cyapa->gen != CYAPA_GEN3) { + dev_err(dev, "unsupported protocol version (%d)", + cyapa->gen); + return -EINVAL; + } + + /* only support product ID starting with CYTRA */ + if (memcmp(cyapa->product_id, unique_str, + sizeof(unique_str) - 1) != 0) { + dev_err(dev, "unsupported product ID (%s)\n", + cyapa->product_id); + return -EINVAL; + } + return 0; + + default: + return -EIO; + } + return 0; +} + +static irqreturn_t cyapa_irq(int irq, void *dev_id) +{ + struct cyapa *cyapa = dev_id; + struct device *dev = &cyapa->client->dev; + struct input_dev *input = cyapa->input; + struct cyapa_reg_data data; + int i; + int ret; + int num_fingers; + + if (device_may_wakeup(dev)) + pm_wakeup_event(dev, 0); + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) + goto out; + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { + goto out; + } + + num_fingers = (data.finger_btn >> 4) & 0x0f; + for (i = 0; i < num_fingers; i++) { + const struct cyapa_touch *touch = &data.touches[i]; + /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ + int slot = touch->id - 1; + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, + ((touch->xy_hi & 0xf0) << 4) | touch->x_lo); + input_report_abs(input, ABS_MT_POSITION_Y, + ((touch->xy_hi & 0x0f) << 8) | touch->y_lo); + input_report_abs(input, ABS_MT_PRESSURE, touch->pressure); + } + + input_mt_sync_frame(input); + + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) + input_report_key(input, BTN_LEFT, + data.finger_btn & OP_DATA_LEFT_BTN); + + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) + input_report_key(input, BTN_MIDDLE, + data.finger_btn & OP_DATA_MIDDLE_BTN); + + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) + input_report_key(input, BTN_RIGHT, + data.finger_btn & OP_DATA_RIGHT_BTN); + + input_sync(input); + +out: + return IRQ_HANDLED; +} + +static u8 cyapa_check_adapter_functionality(struct i2c_client *client) +{ + u8 ret = CYAPA_ADAPTER_FUNC_NONE; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + ret |= CYAPA_ADAPTER_FUNC_I2C; + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + ret |= CYAPA_ADAPTER_FUNC_SMBUS; + return ret; +} + +static int cyapa_create_input_dev(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + int ret; + struct input_dev *input; + + if (!cyapa->physical_size_x || !cyapa->physical_size_y) + return -EINVAL; + + input = cyapa->input = input_allocate_device(); + if (!input) { + dev_err(dev, "allocate memory for input device failed\n"); + return -ENOMEM; + } + + input->name = CYAPA_NAME; + input->phys = cyapa->phys; + input->id.bustype = BUS_I2C; + input->id.version = 1; + input->id.product = 0; /* means any product in eventcomm. */ + input->dev.parent = &cyapa->client->dev; + + input_set_drvdata(input, cyapa); + + __set_bit(EV_ABS, input->evbit); + + /* finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0, + 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0, + 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + input_abs_set_res(input, ABS_MT_POSITION_X, + cyapa->max_abs_x / cyapa->physical_size_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, + cyapa->max_abs_y / cyapa->physical_size_y); + + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) + __set_bit(BTN_LEFT, input->keybit); + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) + __set_bit(BTN_MIDDLE, input->keybit); + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) + __set_bit(BTN_RIGHT, input->keybit); + + if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + + /* handle pointer emulation and unused slots in core */ + ret = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); + if (ret) { + dev_err(dev, "allocate memory for MT slots failed, %d\n", ret); + goto err_free_device; + } + + /* Register the device in input subsystem */ + ret = input_register_device(input); + if (ret) { + dev_err(dev, "input device register failed, %d\n", ret); + goto err_free_device; + } + return 0; + +err_free_device: + input_free_device(input); + cyapa->input = NULL; + return ret; +} + +static int cyapa_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret; + u8 adapter_func; + struct cyapa *cyapa; + struct device *dev = &client->dev; + + adapter_func = cyapa_check_adapter_functionality(client); + if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) { + dev_err(dev, "not a supported I2C/SMBus adapter\n"); + return -EIO; + } + + cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL); + if (!cyapa) { + dev_err(dev, "allocate memory for cyapa failed\n"); + return -ENOMEM; + } + + cyapa->gen = CYAPA_GEN3; + cyapa->client = client; + i2c_set_clientdata(client, cyapa); + sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, + client->addr); + + /* i2c isn't supported, use smbus */ + if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS) + cyapa->smbus = true; + cyapa->state = CYAPA_STATE_NO_DEVICE; + ret = cyapa_check_is_operational(cyapa); + if (ret) { + dev_err(dev, "device not operational, %d\n", ret); + goto err_mem_free; + } + + ret = cyapa_create_input_dev(cyapa); + if (ret) { + dev_err(dev, "create input_dev instance failed, %d\n", ret); + goto err_mem_free; + } + + ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); + if (ret) { + dev_err(dev, "set active power failed, %d\n", ret); + goto err_unregister_device; + } + + cyapa->irq = client->irq; + ret = request_threaded_irq(cyapa->irq, + NULL, + cyapa_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "cyapa", + cyapa); + if (ret) { + dev_err(dev, "IRQ request failed: %d\n, ", ret); + goto err_unregister_device; + } + + return 0; + +err_unregister_device: + input_unregister_device(cyapa->input); +err_mem_free: + kfree(cyapa); + + return ret; +} + +static int cyapa_remove(struct i2c_client *client) +{ + struct cyapa *cyapa = i2c_get_clientdata(client); + + free_irq(cyapa->irq, cyapa); + input_unregister_device(cyapa->input); + cyapa_set_power_mode(cyapa, PWR_MODE_OFF); + kfree(cyapa); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyapa_suspend(struct device *dev) +{ + int ret; + u8 power_mode; + struct cyapa *cyapa = dev_get_drvdata(dev); + + disable_irq(cyapa->irq); + + /* + * Set trackpad device to idle mode if wakeup is allowed, + * otherwise turn off. + */ + power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE + : PWR_MODE_OFF; + ret = cyapa_set_power_mode(cyapa, power_mode); + if (ret < 0) + dev_err(dev, "set power mode failed, %d\n", ret); + + if (device_may_wakeup(dev)) + cyapa->irq_wake = (enable_irq_wake(cyapa->irq) == 0); + return 0; +} + +static int cyapa_resume(struct device *dev) +{ + int ret; + struct cyapa *cyapa = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && cyapa->irq_wake) + disable_irq_wake(cyapa->irq); + + ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); + if (ret) + dev_warn(dev, "resume active power failed, %d\n", ret); + + enable_irq(cyapa->irq); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume); + +static const struct i2c_device_id cyapa_id_table[] = { + { "cyapa", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, cyapa_id_table); + +static struct i2c_driver cyapa_driver = { + .driver = { + .name = "cyapa", + .owner = THIS_MODULE, + .pm = &cyapa_pm_ops, + }, + + .probe = cyapa_probe, + .remove = cyapa_remove, + .id_table = cyapa_id_table, +}; + +module_i2c_driver(cyapa_driver); + +MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver"); +MODULE_AUTHOR("Dudley Du <dudl@cypress.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c new file mode 100644 index 000000000000..1673dc6c8092 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.c @@ -0,0 +1,725 @@ +/* + * Cypress Trackpad PS/2 mouse driver + * + * Copyright (c) 2012 Cypress Semiconductor Corporation. + * + * Author: + * Dudley Du <dudl@cypress.com> + * + * Additional contributors include: + * Kamal Mostafa <kamal@canonical.com> + * Kyle Fazzari <git@status.e4ward.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/serio.h> +#include <linux/libps2.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/sched.h> +#include <linux/wait.h> + +#include "cypress_ps2.h" + +#undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose dump */ + +static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n) +{ + struct cytp_data *cytp = psmouse->private; + cytp->pkt_size = n; +} + +static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200}; +static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03}; + +static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) { + psmouse_dbg(psmouse, + "sending command 0x%02x failed, resp 0x%02x\n", + value & 0xff, ps2dev->nak); + if (ps2dev->nak == CYTP_PS2_RETRY) + return CYTP_PS2_RETRY; + else + return CYTP_PS2_ERROR; + } + +#ifdef CYTP_DEBUG_VERBOSE + psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n", + value & 0xff); +#endif + + return 0; +} + +static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd, + unsigned char data) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + ps2_begin_command(ps2dev); + + do { + /* + * Send extension command byte (0xE8 or 0xF3). + * If sending the command fails, send recovery command + * to make the device return to the ready state. + */ + rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff); + if (rc == CYTP_PS2_RETRY) { + rc = cypress_ps2_sendbyte(psmouse, 0x00); + if (rc == CYTP_PS2_RETRY) + rc = cypress_ps2_sendbyte(psmouse, 0x0a); + } + if (rc == CYTP_PS2_ERROR) + continue; + + rc = cypress_ps2_sendbyte(psmouse, data); + if (rc == CYTP_PS2_RETRY) + rc = cypress_ps2_sendbyte(psmouse, data); + if (rc == CYTP_PS2_ERROR) + continue; + else + break; + } while (--tries > 0); + + ps2_end_command(ps2dev); + + return rc; +} + +static int cypress_ps2_read_cmd_status(struct psmouse *psmouse, + unsigned char cmd, + unsigned char *param) +{ + int rc; + struct ps2dev *ps2dev = &psmouse->ps2dev; + enum psmouse_state old_state; + int pktsize; + + ps2_begin_command(&psmouse->ps2dev); + + old_state = psmouse->state; + psmouse->state = PSMOUSE_CMD_MODE; + psmouse->pktcnt = 0; + + pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3; + memset(param, 0, pktsize); + + rc = cypress_ps2_sendbyte(psmouse, 0xe9); + if (rc < 0) + goto out; + + wait_event_timeout(ps2dev->wait, + (psmouse->pktcnt >= pktsize), + msecs_to_jiffies(CYTP_CMD_TIMEOUT)); + + memcpy(param, psmouse->packet, pktsize); + + psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n", + cmd, pktsize, param); + +out: + psmouse->state = old_state; + psmouse->pktcnt = 0; + + ps2_end_command(&psmouse->ps2dev); + + return rc; +} + +static bool cypress_verify_cmd_state(struct psmouse *psmouse, + unsigned char cmd, unsigned char *param) +{ + bool rate_match = false; + bool resolution_match = false; + int i; + + /* callers will do further checking. */ + if (cmd == CYTP_CMD_READ_CYPRESS_ID || + cmd == CYTP_CMD_STANDARD_MODE || + cmd == CYTP_CMD_READ_TP_METRICS) + return true; + + if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID && + (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) { + for (i = 0; i < sizeof(cytp_resolution); i++) + if (cytp_resolution[i] == param[1]) + resolution_match = true; + + for (i = 0; i < sizeof(cytp_rate); i++) + if (cytp_rate[i] == param[2]) + rate_match = true; + + if (resolution_match && rate_match) + return true; + } + + psmouse_dbg(psmouse, "verify cmd state failed.\n"); + return false; +} + +static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd, + unsigned char *param) +{ + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n", + cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd), + DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd)); + + do { + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd)); + + rc = cypress_ps2_read_cmd_status(psmouse, cmd, param); + if (rc) + continue; + + if (cypress_verify_cmd_state(psmouse, cmd, param)) + return 0; + + } while (--tries > 0); + + return -EIO; +} + +int cypress_detect(struct psmouse *psmouse, bool set_properties) +{ + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) + return -ENODEV; + + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ + if (param[0] != 0x33 || param[1] != 0xCC) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Cypress"; + psmouse->name = "Trackpad"; + } + + return 0; +} + +static int cypress_read_fw_version(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) + return -ENODEV; + + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ + if (param[0] != 0x33 || param[1] != 0xCC) + return -ENODEV; + + cytp->fw_version = param[2] & FW_VERSION_MASX; + cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; + + psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version); + psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n", + cytp->tp_metrics_supported); + + return 0; +} + +static int cypress_read_tp_metrics(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[8]; + + /* set default values for tp metrics. */ + cytp->tp_width = CYTP_DEFAULT_WIDTH; + cytp->tp_high = CYTP_DEFAULT_HIGH; + cytp->tp_max_abs_x = CYTP_ABS_MAX_X; + cytp->tp_max_abs_y = CYTP_ABS_MAX_Y; + cytp->tp_min_pressure = CYTP_MIN_PRESSURE; + cytp->tp_max_pressure = CYTP_MAX_PRESSURE; + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; + + memset(param, 0, sizeof(param)); + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) { + /* Update trackpad parameters. */ + cytp->tp_max_abs_x = (param[1] << 8) | param[0]; + cytp->tp_max_abs_y = (param[3] << 8) | param[2]; + cytp->tp_min_pressure = param[4]; + cytp->tp_max_pressure = param[5]; + } + + if (!cytp->tp_max_pressure || + cytp->tp_max_pressure < cytp->tp_min_pressure || + !cytp->tp_width || !cytp->tp_high || + !cytp->tp_max_abs_x || + cytp->tp_max_abs_x < cytp->tp_width || + !cytp->tp_max_abs_y || + cytp->tp_max_abs_y < cytp->tp_high) + return -EINVAL; + + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; + +#ifdef CYTP_DEBUG_VERBOSE + psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n"); + psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width); + psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high); + psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x); + psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y); + psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure); + psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure); + psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x); + psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y); + + psmouse_dbg(psmouse, "tp_type_APA = %d\n", + (param[6] & TP_METRICS_BIT_APA) ? 1 : 0); + psmouse_dbg(psmouse, "tp_type_MTG = %d\n", + (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0); + psmouse_dbg(psmouse, "tp_palm = %d\n", + (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0); + psmouse_dbg(psmouse, "tp_stubborn = %d\n", + (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0); + psmouse_dbg(psmouse, "tp_1f_jitter = %d\n", + (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2); + psmouse_dbg(psmouse, "tp_2f_jitter = %d\n", + (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4); + psmouse_dbg(psmouse, "tp_1f_spike = %d\n", + param[7] & TP_METRICS_BIT_1F_SPIKE); + psmouse_dbg(psmouse, "tp_2f_spike = %d\n", + (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2); + psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n", + (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4); +#endif + + return 0; +} + +static int cypress_query_hardware(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + int ret; + + ret = cypress_read_fw_version(psmouse); + if (ret) + return ret; + + if (cytp->tp_metrics_supported) { + ret = cypress_read_tp_metrics(psmouse); + if (ret) + return ret; + } + + return 0; +} + +static int cypress_set_absolute_mode(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0) + return -1; + + cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK) + | CYTP_BIT_ABS_PRESSURE; + cypress_set_packet_size(psmouse, 5); + + return 0; +} + +/* + * Reset trackpad device. + * This is also the default mode when trackpad powered on. + */ +static void cypress_reset(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + + cytp->mode = 0; + + psmouse_reset(psmouse); +} + +static int cypress_set_input_params(struct input_dev *input, + struct cytp_data *cytp) +{ + int ret; + + if (!cytp->tp_res_x || !cytp->tp_res_y) + return -EINVAL; + + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0); + input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, + cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0); + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0); + + /* finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS, + INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK); + if (ret < 0) + return ret; + + __set_bit(INPUT_PROP_SEMI_MT, input->propbit); + + input_abs_set_res(input, ABS_X, cytp->tp_res_x); + input_abs_set_res(input, ABS_Y, cytp->tp_res_y); + + input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y); + + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); + __set_bit(BTN_TOOL_QUADTAP, input->keybit); + __set_bit(BTN_TOOL_QUINTTAP, input->keybit); + + __clear_bit(EV_REL, input->evbit); + __clear_bit(REL_X, input->relbit); + __clear_bit(REL_Y, input->relbit); + + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + __set_bit(BTN_MIDDLE, input->keybit); + + input_set_drvdata(input, cytp); + + return 0; +} + +static int cypress_get_finger_count(unsigned char header_byte) +{ + unsigned char bits6_7; + int finger_count; + + bits6_7 = header_byte >> 6; + finger_count = bits6_7 & 0x03; + + if (finger_count == 1) + return 1; + + if (header_byte & ABS_HSCROLL_BIT) { + /* HSCROLL gets added on to 0 finger count. */ + switch (finger_count) { + case 0: return 4; + case 2: return 5; + default: + /* Invalid contact (e.g. palm). Ignore it. */ + return -1; + } + } + + return finger_count; +} + + +static int cypress_parse_packet(struct psmouse *psmouse, + struct cytp_data *cytp, struct cytp_report_data *report_data) +{ + unsigned char *packet = psmouse->packet; + unsigned char header_byte = packet[0]; + int contact_cnt; + + memset(report_data, 0, sizeof(struct cytp_report_data)); + + contact_cnt = cypress_get_finger_count(header_byte); + + if (contact_cnt < 0) /* e.g. palm detect */ + return -EINVAL; + + report_data->contact_cnt = contact_cnt; + + report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; + + if (report_data->contact_cnt == 1) { + report_data->contacts[0].x = + ((packet[1] & 0x70) << 4) | packet[2]; + report_data->contacts[0].y = + ((packet[1] & 0x07) << 8) | packet[3]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[0].z = packet[4]; + + } else if (report_data->contact_cnt >= 2) { + report_data->contacts[0].x = + ((packet[1] & 0x70) << 4) | packet[2]; + report_data->contacts[0].y = + ((packet[1] & 0x07) << 8) | packet[3]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[0].z = packet[4]; + + report_data->contacts[1].x = + ((packet[5] & 0xf0) << 4) | packet[6]; + report_data->contacts[1].y = + ((packet[5] & 0x0f) << 8) | packet[7]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[1].z = report_data->contacts[0].z; + } + + report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0; + report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0; + + /* + * This is only true if one of the mouse buttons were tapped. Make + * sure it doesn't turn into a click. The regular tap-to-click + * functionality will handle that on its own. If we don't do this, + * disabling tap-to-click won't affect the mouse button zones. + */ + if (report_data->tap) + report_data->left = 0; + +#ifdef CYTP_DEBUG_VERBOSE + { + int i; + int n = report_data->contact_cnt; + psmouse_dbg(psmouse, "Dump parsed report data as below:\n"); + psmouse_dbg(psmouse, "contact_cnt = %d\n", + report_data->contact_cnt); + if (n > CYTP_MAX_MT_SLOTS) + n = CYTP_MAX_MT_SLOTS; + for (i = 0; i < n; i++) + psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i, + report_data->contacts[i].x, + report_data->contacts[i].y, + report_data->contacts[i].z); + psmouse_dbg(psmouse, "left = %d\n", report_data->left); + psmouse_dbg(psmouse, "right = %d\n", report_data->right); + psmouse_dbg(psmouse, "middle = %d\n", report_data->middle); + } +#endif + + return 0; +} + +static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt) +{ + int i; + struct input_dev *input = psmouse->dev; + struct cytp_data *cytp = psmouse->private; + struct cytp_report_data report_data; + struct cytp_contact *contact; + struct input_mt_pos pos[CYTP_MAX_MT_SLOTS]; + int slots[CYTP_MAX_MT_SLOTS]; + int n; + + if (cypress_parse_packet(psmouse, cytp, &report_data)) + return; + + n = report_data.contact_cnt; + + if (n > CYTP_MAX_MT_SLOTS) + n = CYTP_MAX_MT_SLOTS; + + for (i = 0; i < n; i++) { + contact = &report_data.contacts[i]; + pos[i].x = contact->x; + pos[i].y = contact->y; + } + + input_mt_assign_slots(input, slots, pos, n); + + for (i = 0; i < n; i++) { + contact = &report_data.contacts[i]; + input_mt_slot(input, slots[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, contact->x); + input_report_abs(input, ABS_MT_POSITION_Y, contact->y); + input_report_abs(input, ABS_MT_PRESSURE, contact->z); + } + + input_mt_sync_frame(input); + + input_mt_report_finger_count(input, report_data.contact_cnt); + + input_report_key(input, BTN_LEFT, report_data.left); + input_report_key(input, BTN_RIGHT, report_data.right); + input_report_key(input, BTN_MIDDLE, report_data.middle); + + input_sync(input); +} + +static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) +{ + int contact_cnt; + int index = psmouse->pktcnt - 1; + unsigned char *packet = psmouse->packet; + struct cytp_data *cytp = psmouse->private; + + if (index < 0 || index > cytp->pkt_size) + return PSMOUSE_BAD_DATA; + + if (index == 0 && (packet[0] & 0xfc) == 0) { + /* call packet process for reporting finger leave. */ + cypress_process_packet(psmouse, 1); + return PSMOUSE_FULL_PACKET; + } + + /* + * Perform validation (and adjust packet size) based only on the + * first byte; allow all further bytes through. + */ + if (index != 0) + return PSMOUSE_GOOD_DATA; + + /* + * If absolute/relative mode bit has not been set yet, just pass + * the byte through. + */ + if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0) + return PSMOUSE_GOOD_DATA; + + if ((packet[0] & 0x08) == 0x08) + return PSMOUSE_BAD_DATA; + + contact_cnt = cypress_get_finger_count(packet[0]); + + if (contact_cnt < 0) + return PSMOUSE_BAD_DATA; + + if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); + else + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5); + + return PSMOUSE_GOOD_DATA; +} + +static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + + if (psmouse->pktcnt >= cytp->pkt_size) { + cypress_process_packet(psmouse, 0); + return PSMOUSE_FULL_PACKET; + } + + return cypress_validate_byte(psmouse); +} + +static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + struct cytp_data *cytp = psmouse->private; + + if (rate >= 80) { + psmouse->rate = 80; + cytp->mode |= CYTP_BIT_HIGH_RATE; + } else { + psmouse->rate = 40; + cytp->mode &= ~CYTP_BIT_HIGH_RATE; + } + + ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate, + PSMOUSE_CMD_SETRATE); +} + +static void cypress_disconnect(struct psmouse *psmouse) +{ + cypress_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; +} + +static int cypress_reconnect(struct psmouse *psmouse) +{ + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + do { + cypress_reset(psmouse); + rc = cypress_detect(psmouse, false); + } while (rc && (--tries > 0)); + + if (rc) { + psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n"); + return -1; + } + + if (cypress_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n"); + return -1; + } + + return 0; +} + +int cypress_init(struct psmouse *psmouse) +{ + struct cytp_data *cytp; + + cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL); + psmouse->private = (void *)cytp; + if (cytp == NULL) + return -ENOMEM; + + cypress_reset(psmouse); + + psmouse->pktsize = 8; + + if (cypress_query_hardware(psmouse)) { + psmouse_err(psmouse, "Unable to query Trackpad hardware.\n"); + goto err_exit; + } + + if (cypress_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n"); + goto err_exit; + } + + if (cypress_set_input_params(psmouse->dev, cytp) < 0) { + psmouse_err(psmouse, "init: Unable to set input params.\n"); + goto err_exit; + } + + psmouse->model = 1; + psmouse->protocol_handler = cypress_protocol_handler; + psmouse->set_rate = cypress_set_rate; + psmouse->disconnect = cypress_disconnect; + psmouse->reconnect = cypress_reconnect; + psmouse->cleanup = cypress_reset; + psmouse->resync_time = 0; + + return 0; + +err_exit: + /* + * Reset Cypress Trackpad as a standard mouse. Then + * let psmouse driver commmunicating with it as default PS2 mouse. + */ + cypress_reset(psmouse); + + psmouse->private = NULL; + kfree(cytp); + + return -1; +} + +bool cypress_supported(void) +{ + return true; +} diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h new file mode 100644 index 000000000000..4720f21d2d70 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.h @@ -0,0 +1,191 @@ +#ifndef _CYPRESS_PS2_H +#define _CYPRESS_PS2_H + +#include "psmouse.h" + +#define CMD_BITS_MASK 0x03 +#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s)) + +#define ENCODE_CMD(aa, bb, cc, dd) \ + (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0)) +#define CYTP_CMD_ABS_NO_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 0) +#define CYTP_CMD_ABS_WITH_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 1) +#define CYTP_CMD_SMBUS_MODE ENCODE_CMD(0, 1, 1, 0) +#define CYTP_CMD_STANDARD_MODE ENCODE_CMD(0, 2, 0, 0) /* not implemented yet. */ +#define CYTP_CMD_CYPRESS_REL_MODE ENCODE_CMD(1, 1, 1, 1) /* not implemented yet. */ +#define CYTP_CMD_READ_CYPRESS_ID ENCODE_CMD(0, 0, 0, 0) +#define CYTP_CMD_READ_TP_METRICS ENCODE_CMD(0, 0, 0, 1) +#define CYTP_CMD_SET_HSCROLL_WIDTH(w) ENCODE_CMD(1, 1, 0, (w)) +#define CYTP_CMD_SET_HSCROLL_MASK ENCODE_CMD(1, 1, 0, 0) +#define CYTP_CMD_SET_VSCROLL_WIDTH(w) ENCODE_CMD(1, 2, 0, (w)) +#define CYTP_CMD_SET_VSCROLL_MASK ENCODE_CMD(1, 2, 0, 0) +#define CYTP_CMD_SET_PALM_GEOMETRY(e) ENCODE_CMD(1, 2, 1, (e)) +#define CYTP_CMD_PALM_GEMMETRY_MASK ENCODE_CMD(1, 2, 1, 0) +#define CYTP_CMD_SET_PALM_SENSITIVITY(s) ENCODE_CMD(1, 2, 2, (s)) +#define CYTP_CMD_PALM_SENSITIVITY_MASK ENCODE_CMD(1, 2, 2, 0) +#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s) ENCODE_CMD(1, 3, ((s) >> 2), (s)) +#define CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0) +#define CYTP_CMD_REQUEST_BASELINE_STATUS ENCODE_CMD(2, 0, 0, 1) +#define CYTP_CMD_REQUEST_RECALIBRATION ENCODE_CMD(2, 0, 0, 3) + +#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK) +#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK) +#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK) +#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK) + +/* Cypress trackpad working mode. */ +#define CYTP_BIT_ABS_PRESSURE (1 << 3) +#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2) +#define CYTP_BIT_CYPRESS_REL (1 << 1) +#define CYTP_BIT_STANDARD_REL (1 << 0) +#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL) +#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE) +#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK) + +#define CYTP_BIT_HIGH_RATE (1 << 4) +/* + * report mode bit is set, firmware working in Remote Mode. + * report mode bit is cleared, firmware working in Stream Mode. + */ +#define CYTP_BIT_REPORT_MODE (1 << 5) + +/* scrolling width values for set HSCROLL and VSCROLL width command. */ +#define SCROLL_WIDTH_NARROW 1 +#define SCROLL_WIDTH_NORMAL 2 +#define SCROLL_WIDTH_WIDE 3 + +#define PALM_GEOMETRY_ENABLE 1 +#define PALM_GEOMETRY_DISABLE 0 + +#define TP_METRICS_MASK 0x80 +#define FW_VERSION_MASX 0x7f +#define FW_VER_HIGH_MASK 0x70 +#define FW_VER_LOW_MASK 0x0f + +/* Times to retry a ps2_command and millisecond delay between tries. */ +#define CYTP_PS2_CMD_TRIES 3 +#define CYTP_PS2_CMD_DELAY 500 + +/* time out for PS/2 command only in milliseconds. */ +#define CYTP_CMD_TIMEOUT 200 +#define CYTP_DATA_TIMEOUT 30 + +#define CYTP_EXT_CMD 0xe8 +#define CYTP_PS2_RETRY 0xfe +#define CYTP_PS2_ERROR 0xfc + +#define CYTP_RESP_RETRY 0x01 +#define CYTP_RESP_ERROR 0xfe + + +#define CYTP_105001_WIDTH 97 /* Dell XPS 13 */ +#define CYTP_105001_HIGH 59 +#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH) +#define CYTP_DEFAULT_HIGH (CYTP_105001_HIGH) + +#define CYTP_ABS_MAX_X 1600 +#define CYTP_ABS_MAX_Y 900 +#define CYTP_MAX_PRESSURE 255 +#define CYTP_MIN_PRESSURE 0 + +/* header byte bits of relative package. */ +#define BTN_LEFT_BIT 0x01 +#define BTN_RIGHT_BIT 0x02 +#define BTN_MIDDLE_BIT 0x04 +#define REL_X_SIGN_BIT 0x10 +#define REL_Y_SIGN_BIT 0x20 + +/* header byte bits of absolute package. */ +#define ABS_VSCROLL_BIT 0x10 +#define ABS_HSCROLL_BIT 0x20 +#define ABS_MULTIFINGER_TAP 0x04 +#define ABS_EDGE_MOTION_MASK 0x80 + +#define DFLT_RESP_BITS_VALID 0x88 /* SMBus bit should not be set. */ +#define DFLT_RESP_SMBUS_BIT 0x80 +#define DFLT_SMBUS_MODE 0x80 +#define DFLT_PS2_MODE 0x00 +#define DFLT_RESP_BIT_MODE 0x40 +#define DFLT_RESP_REMOTE_MODE 0x40 +#define DFLT_RESP_STREAM_MODE 0x00 +#define DFLT_RESP_BIT_REPORTING 0x20 +#define DFLT_RESP_BIT_SCALING 0x10 + +#define TP_METRICS_BIT_PALM 0x80 +#define TP_METRICS_BIT_STUBBORN 0x40 +#define TP_METRICS_BIT_2F_JITTER 0x30 +#define TP_METRICS_BIT_1F_JITTER 0x0c +#define TP_METRICS_BIT_APA 0x02 +#define TP_METRICS_BIT_MTG 0x01 +#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0 +#define TP_METRICS_BIT_2F_SPIKE 0x0c +#define TP_METRICS_BIT_1F_SPIKE 0x03 + +/* bits of first byte response of E9h-Status Request command. */ +#define RESP_BTN_RIGHT_BIT 0x01 +#define RESP_BTN_MIDDLE_BIT 0x02 +#define RESP_BTN_LEFT_BIT 0x04 +#define RESP_SCALING_BIT 0x10 +#define RESP_ENABLE_BIT 0x20 +#define RESP_REMOTE_BIT 0x40 +#define RESP_SMBUS_BIT 0x80 + +#define CYTP_MAX_MT_SLOTS 2 + +struct cytp_contact { + int x; + int y; + int z; /* also named as touch pressure. */ +}; + +/* The structure of Cypress Trackpad event data. */ +struct cytp_report_data { + int contact_cnt; + struct cytp_contact contacts[CYTP_MAX_MT_SLOTS]; + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + unsigned int tap:1; /* multi-finger tap detected. */ +}; + +/* The structure of Cypress Trackpad device private data. */ +struct cytp_data { + int fw_version; + + int pkt_size; + int mode; + + int tp_min_pressure; + int tp_max_pressure; + int tp_width; /* X direction physical size in mm. */ + int tp_high; /* Y direction physical size in mm. */ + int tp_max_abs_x; /* Max X absolute units that can be reported. */ + int tp_max_abs_y; /* Max Y absolute units that can be reported. */ + + int tp_res_x; /* X resolution in units/mm. */ + int tp_res_y; /* Y resolution in units/mm. */ + + int tp_metrics_supported; +}; + + +#ifdef CONFIG_MOUSE_PS2_CYPRESS +int cypress_detect(struct psmouse *psmouse, bool set_properties); +int cypress_init(struct psmouse *psmouse); +bool cypress_supported(void); +#else +inline int cypress_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +inline int cypress_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +inline bool cypress_supported(void) +{ + return 0; +} +#endif /* CONFIG_MOUSE_PS2_CYPRESS */ + +#endif /* _CYPRESS_PS2_H */ diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 22fe2547e169..cff065f6261c 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -34,6 +34,7 @@ #include "touchkit_ps2.h" #include "elantech.h" #include "sentelic.h" +#include "cypress_ps2.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -759,6 +760,28 @@ static int psmouse_extensions(struct psmouse *psmouse, } /* + * Try Cypress Trackpad. + * Must try it before Finger Sensing Pad because Finger Sensing Pad probe + * upsets some modules of Cypress Trackpads. + */ + if (max_proto > PSMOUSE_IMEX && + cypress_detect(psmouse, set_properties) == 0) { + if (cypress_supported()) { + if (cypress_init(psmouse) == 0) + return PSMOUSE_CYPRESS; + + /* + * Finger Sensing Pad probe upsets some modules of + * Cypress Trackpad, must avoid Finger Sensing Pad + * probe if Cypress Trackpad device detected. + */ + return PSMOUSE_PS2; + } + + max_proto = PSMOUSE_IMEX; + } + +/* * Try ALPS TouchPad */ if (max_proto > PSMOUSE_IMEX) { @@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .alias = "thinkps", .detect = thinking_detect, }, +#ifdef CONFIG_MOUSE_PS2_CYPRESS + { + .type = PSMOUSE_CYPRESS, + .name = "CyPS/2", + .alias = "cypress", + .detect = cypress_detect, + .init = cypress_init, + }, +#endif { .type = PSMOUSE_GENPS, .name = "GenPS/2", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index fe1df231ba4c..2f0b39d59a9b 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -95,6 +95,7 @@ enum psmouse_type { PSMOUSE_ELANTECH, PSMOUSE_FSP, PSMOUSE_SYNAPTICS_RELATIVE, + PSMOUSE_CYPRESS, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 12d12ca3fee0..2f78538e09d0 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -722,11 +722,13 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, default: /* * If the finger slot contained in SGM is valid, and either - * hasn't changed, or is new, then report SGM in MTB slot 0. + * hasn't changed, or is new, or the old SGM has now moved to + * AGM, then report SGM in MTB slot 0. * Otherwise, empty MTB slot 0. */ if (mt_state->sgm != -1 && - (mt_state->sgm == old->sgm || old->sgm == -1)) + (mt_state->sgm == old->sgm || + old->sgm == -1 || mt_state->agm == old->sgm)) synaptics_report_slot(dev, 0, sgm); else synaptics_report_slot(dev, 0, NULL); @@ -735,9 +737,31 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, * If the finger slot contained in AGM is valid, and either * hasn't changed, or is new, then report AGM in MTB slot 1. * Otherwise, empty MTB slot 1. + * + * However, in the case where the AGM is new, make sure that + * that it is either the same as the old SGM, or there was no + * SGM. + * + * Otherwise, if the SGM was just 1, and the new AGM is 2, then + * the new AGM will keep the old SGM's tracking ID, which can + * cause apparent drumroll. This happens if in the following + * valid finger sequence: + * + * Action SGM AGM (MTB slot:Contact) + * 1. Touch contact 0 (0:0) + * 2. Touch contact 1 (0:0, 1:1) + * 3. Lift contact 0 (1:1) + * 4. Touch contacts 2,3 (0:2, 1:3) + * + * In step 4, contact 3, in AGM must not be given the same + * tracking ID as contact 1 had in step 3. To avoid this, + * the first agm with contact 3 is dropped and slot 1 is + * invalidated (tracking ID = -1). */ if (mt_state->agm != -1 && - (mt_state->agm == old->agm || old->agm == -1)) + (mt_state->agm == old->agm || + (old->agm == -1 && + (old->sgm == -1 || mt_state->agm == old->sgm)))) synaptics_report_slot(dev, 1, agm); else synaptics_report_slot(dev, 1, NULL); @@ -1247,11 +1271,11 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { - input_mt_init_slots(dev, 2, 0); set_abs_position_params(dev, priv, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_mt_init_slots(dev, 2, INPUT_MT_POINTER); /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); |