summaryrefslogtreecommitdiffstats
path: root/drivers/input/mouse
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2013-02-19 22:05:39 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2013-02-19 22:05:39 -0800
commit2d9f0d964be94fd51c7303288c6f9c88bf2381fe (patch)
tree2631c3e82abc145a6e9c8e2c18687833c71de012 /drivers/input/mouse
parent9937c026820baabd1e908a9c1e6bdc846293000a (diff)
parent005a69d632cd8694061c2dd27492fe874780b5ee (diff)
downloadblackbird-op-linux-2d9f0d964be94fd51c7303288c6f9c88bf2381fe.tar.gz
blackbird-op-linux-2d9f0d964be94fd51c7303288c6f9c88bf2381fe.zip
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/Kconfig22
-rw-r--r--drivers/input/mouse/Makefile2
-rw-r--r--drivers/input/mouse/alps.c773
-rw-r--r--drivers/input/mouse/alps.h145
-rw-r--r--drivers/input/mouse/cyapa.c973
-rw-r--r--drivers/input/mouse/cypress_ps2.c725
-rw-r--r--drivers/input/mouse/cypress_ps2.h191
-rw-r--r--drivers/input/mouse/psmouse-base.c32
-rw-r--r--drivers/input/mouse/psmouse.h1
-rw-r--r--drivers/input/mouse/synaptics.c32
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);
OpenPOWER on IntegriCloud