xref: /openbmc/linux/drivers/input/mouse/alps.c (revision 20bea68b)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * ALPS touchpad PS/2 mouse driver
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5963f626dSPeter Osterlund  * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
61da177e4SLinus Torvalds  * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
71da177e4SLinus Torvalds  * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
81d9f2626SSebastian Kapfer  * Copyright (c) 2009 Sebastian Kapfer <sebastian_kapfer@gmx.net>
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * ALPS detection, tap switching and status querying info is taken from
111da177e4SLinus Torvalds  * tpconfig utility (by C. Scott Ananian and Bruce Kall).
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify it
141da177e4SLinus Torvalds  * under the terms of the GNU General Public License version 2 as published by
151da177e4SLinus Torvalds  * the Free Software Foundation.
161da177e4SLinus Torvalds  */
171da177e4SLinus Torvalds 
185a0e3ad6STejun Heo #include <linux/slab.h>
191da177e4SLinus Torvalds #include <linux/input.h>
2001ce661fSSeth Forshee #include <linux/input/mt.h>
211da177e4SLinus Torvalds #include <linux/serio.h>
221da177e4SLinus Torvalds #include <linux/libps2.h>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds #include "psmouse.h"
251da177e4SLinus Torvalds #include "alps.h"
261da177e4SLinus Torvalds 
2725bded7cSSeth Forshee /*
2825bded7cSSeth Forshee  * Definitions for ALPS version 3 and 4 command mode protocol
2925bded7cSSeth Forshee  */
3025bded7cSSeth Forshee #define ALPS_CMD_NIBBLE_10	0x01f2
3125bded7cSSeth Forshee 
32cd401204SKevin Cernekee #define ALPS_REG_BASE_RUSHMORE	0xc2c0
33cd401204SKevin Cernekee #define ALPS_REG_BASE_PINNACLE	0x0000
34cd401204SKevin Cernekee 
3525bded7cSSeth Forshee static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
3625bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETPOLL,		0x00 }, /* 0 */
3725bded7cSSeth Forshee 	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
3825bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* 2 */
3925bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 3 */
4025bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 4 */
4125bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 5 */
4225bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 6 */
4325bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 7 */
4425bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 8 */
4525bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 9 */
4625bded7cSSeth Forshee 	{ ALPS_CMD_NIBBLE_10,		0x00 }, /* a */
4725bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x00 }, /* b */
4825bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x01 }, /* c */
4925bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x02 }, /* d */
5025bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x03 }, /* e */
5125bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */
5225bded7cSSeth Forshee };
5325bded7cSSeth Forshee 
5425bded7cSSeth Forshee static const struct alps_nibble_commands alps_v4_nibble_commands[] = {
5525bded7cSSeth Forshee 	{ PSMOUSE_CMD_ENABLE,		0x00 }, /* 0 */
5625bded7cSSeth Forshee 	{ PSMOUSE_CMD_RESET_DIS,	0x00 }, /* 1 */
5725bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* 2 */
5825bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 3 */
5925bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 4 */
6025bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 5 */
6125bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 6 */
6225bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 7 */
6325bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 8 */
6425bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 9 */
6525bded7cSSeth Forshee 	{ ALPS_CMD_NIBBLE_10,		0x00 }, /* a */
6625bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x00 }, /* b */
6725bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x01 }, /* c */
6825bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x02 }, /* d */
6925bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETRES,		0x03 }, /* e */
7025bded7cSSeth Forshee 	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */
7125bded7cSSeth Forshee };
7225bded7cSSeth Forshee 
7395f75e91SYunkang Tang static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
7495f75e91SYunkang Tang 	{ PSMOUSE_CMD_ENABLE,		0x00 }, /* 0 */
7595f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRATE,		0x0a }, /* 1 */
7695f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRATE,		0x14 }, /* 2 */
7795f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRATE,		0x28 }, /* 3 */
7895f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRATE,		0x3c }, /* 4 */
7995f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRATE,		0x50 }, /* 5 */
8095f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRATE,		0x64 }, /* 6 */
8195f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRATE,		0xc8 }, /* 7 */
8295f75e91SYunkang Tang 	{ PSMOUSE_CMD_GETID,		0x00 }, /* 8 */
8395f75e91SYunkang Tang 	{ PSMOUSE_CMD_GETINFO,		0x00 }, /* 9 */
8495f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRES,		0x00 }, /* a */
8595f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRES,		0x01 }, /* b */
8695f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRES,		0x02 }, /* c */
8795f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETRES,		0x03 }, /* d */
8895f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETSCALE21,	0x00 }, /* e */
8995f75e91SYunkang Tang 	{ PSMOUSE_CMD_SETSCALE11,	0x00 }, /* f */
9095f75e91SYunkang Tang };
9195f75e91SYunkang Tang 
9225bded7cSSeth Forshee 
9371bb21b6SMaxim Levitsky #define ALPS_DUALPOINT		0x02	/* touchpad has trackstick */
9471bb21b6SMaxim Levitsky #define ALPS_PASS		0x04	/* device has a pass-through port */
9571bb21b6SMaxim Levitsky 
9671bb21b6SMaxim Levitsky #define ALPS_WHEEL		0x08	/* hardware wheel present */
9771bb21b6SMaxim Levitsky #define ALPS_FW_BK_1		0x10	/* front & back buttons present */
9871bb21b6SMaxim Levitsky #define ALPS_FW_BK_2		0x20	/* front & back buttons present */
9971bb21b6SMaxim Levitsky #define ALPS_FOUR_BUTTONS	0x40	/* 4 direction button present */
1001d9f2626SSebastian Kapfer #define ALPS_PS2_INTERLEAVED	0x80	/* 3-byte PS/2 packet interleaved with
1011d9f2626SSebastian Kapfer 					   6-byte ALPS packet */
1021da177e4SLinus Torvalds 
103e38de678SHelge Deller static const struct alps_model_info alps_model_data[] = {
10425bded7cSSeth Forshee 	{ { 0x32, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Toshiba Salellite Pro M10 */
10525bded7cSSeth Forshee 	{ { 0x33, 0x02, 0x0a },	0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 },				/* UMAX-530T */
10625bded7cSSeth Forshee 	{ { 0x53, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
10725bded7cSSeth Forshee 	{ { 0x53, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
10825bded7cSSeth Forshee 	{ { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },				/* HP ze1115 */
10925bded7cSSeth Forshee 	{ { 0x63, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
11025bded7cSSeth Forshee 	{ { 0x63, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
11125bded7cSSeth Forshee 	{ { 0x63, 0x02, 0x28 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 },		/* Fujitsu Siemens S6010 */
11225bded7cSSeth Forshee 	{ { 0x63, 0x02, 0x3c },	0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL },			/* Toshiba Satellite S2400-103 */
11325bded7cSSeth Forshee 	{ { 0x63, 0x02, 0x50 },	0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 },		/* NEC Versa L320 */
11425bded7cSSeth Forshee 	{ { 0x63, 0x02, 0x64 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
11525bded7cSSeth Forshee 	{ { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* Dell Latitude D800 */
11625bded7cSSeth Forshee 	{ { 0x73, 0x00, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT },		/* ThinkPad R61 8918-5QG */
11725bded7cSSeth Forshee 	{ { 0x73, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
11825bded7cSSeth Forshee 	{ { 0x73, 0x02, 0x14 },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 },		/* Ahtec Laptop */
11925bded7cSSeth Forshee 	{ { 0x20, 0x02, 0x0e },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },	/* XXX */
12025bded7cSSeth Forshee 	{ { 0x22, 0x02, 0x0a },	0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
12125bded7cSSeth Forshee 	{ { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT },	/* Dell Latitude D600 */
1221d9f2626SSebastian Kapfer 	/* Dell Latitude E5500, E6400, E6500, Precision M4400 */
12325bded7cSSeth Forshee 	{ { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf,
1241d9f2626SSebastian Kapfer 		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
12595f75e91SYunkang Tang 	{ { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT },		/* Dell XT2 */
12625bded7cSSeth Forshee 	{ { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },		/* Dell Vostro 1400 */
12725bded7cSSeth Forshee 	{ { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff,
128eb8bff85SThomas Bächler 		ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },				/* Toshiba Tecra A11-11L */
12925bded7cSSeth Forshee 	{ { 0x73, 0x02, 0x64 },	0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 },
1301da177e4SLinus Torvalds };
1311da177e4SLinus Torvalds 
13224af5cb9SKevin Cernekee static void alps_set_abs_params_st(struct alps_data *priv,
13324af5cb9SKevin Cernekee 				   struct input_dev *dev1);
13424af5cb9SKevin Cernekee static void alps_set_abs_params_mt(struct alps_data *priv,
13524af5cb9SKevin Cernekee 				   struct input_dev *dev1);
13624af5cb9SKevin Cernekee 
1371da177e4SLinus Torvalds /*
1381da177e4SLinus Torvalds  * XXX - this entry is suspicious. First byte has zero lower nibble,
1391da177e4SLinus Torvalds  * which is what a normal mouse would report. Also, the value 0x0e
1401da177e4SLinus Torvalds  * isn't valid per PS/2 spec.
1411da177e4SLinus Torvalds  */
1421da177e4SLinus Torvalds 
143d4b347b2SSeth Forshee /* Packet formats are described in Documentation/input/alps.txt */
1441da177e4SLinus Torvalds 
14599df65e7SKevin Cernekee static bool alps_is_valid_first_byte(struct alps_data *priv,
1461d9f2626SSebastian Kapfer 				     unsigned char data)
1471d9f2626SSebastian Kapfer {
14899df65e7SKevin Cernekee 	return (data & priv->mask0) == priv->byte0;
1491d9f2626SSebastian Kapfer }
1501d9f2626SSebastian Kapfer 
1511d9f2626SSebastian Kapfer static void alps_report_buttons(struct psmouse *psmouse,
1521d9f2626SSebastian Kapfer 				struct input_dev *dev1, struct input_dev *dev2,
1531d9f2626SSebastian Kapfer 				int left, int right, int middle)
1541d9f2626SSebastian Kapfer {
1551d9f2626SSebastian Kapfer 	struct input_dev *dev;
1561d9f2626SSebastian Kapfer 
1571d9f2626SSebastian Kapfer 	/*
1581d9f2626SSebastian Kapfer 	 * If shared button has already been reported on the
1591d9f2626SSebastian Kapfer 	 * other device (dev2) then this event should be also
1601d9f2626SSebastian Kapfer 	 * sent through that device.
1611d9f2626SSebastian Kapfer 	 */
1621d9f2626SSebastian Kapfer 	dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1;
1631d9f2626SSebastian Kapfer 	input_report_key(dev, BTN_LEFT, left);
1641d9f2626SSebastian Kapfer 
1651d9f2626SSebastian Kapfer 	dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1;
1661d9f2626SSebastian Kapfer 	input_report_key(dev, BTN_RIGHT, right);
1671d9f2626SSebastian Kapfer 
1681d9f2626SSebastian Kapfer 	dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1;
1691d9f2626SSebastian Kapfer 	input_report_key(dev, BTN_MIDDLE, middle);
1701d9f2626SSebastian Kapfer 
1711d9f2626SSebastian Kapfer 	/*
1721d9f2626SSebastian Kapfer 	 * Sync the _other_ device now, we'll do the first
1731d9f2626SSebastian Kapfer 	 * device later once we report the rest of the events.
1741d9f2626SSebastian Kapfer 	 */
1751d9f2626SSebastian Kapfer 	input_sync(dev2);
1761d9f2626SSebastian Kapfer }
1771d9f2626SSebastian Kapfer 
17825bded7cSSeth Forshee static void alps_process_packet_v1_v2(struct psmouse *psmouse)
1791da177e4SLinus Torvalds {
1801da177e4SLinus Torvalds 	struct alps_data *priv = psmouse->private;
1811da177e4SLinus Torvalds 	unsigned char *packet = psmouse->packet;
1822e5b636bSDmitry Torokhov 	struct input_dev *dev = psmouse->dev;
1832e5b636bSDmitry Torokhov 	struct input_dev *dev2 = priv->dev2;
1841da177e4SLinus Torvalds 	int x, y, z, ges, fin, left, right, middle;
185c30b4c10SIvan Casado Ruiz 	int back = 0, forward = 0;
1861da177e4SLinus Torvalds 
18799df65e7SKevin Cernekee 	if (priv->proto_version == ALPS_PROTO_V1) {
188d2f4012fSYotam Medini 		left = packet[2] & 0x10;
189d2f4012fSYotam Medini 		right = packet[2] & 0x08;
1901da177e4SLinus Torvalds 		middle = 0;
1911da177e4SLinus Torvalds 		x = packet[1] | ((packet[0] & 0x07) << 7);
1921da177e4SLinus Torvalds 		y = packet[4] | ((packet[3] & 0x07) << 7);
1931da177e4SLinus Torvalds 		z = packet[5];
1941da177e4SLinus Torvalds 	} else {
1951da177e4SLinus Torvalds 		left = packet[3] & 1;
1961da177e4SLinus Torvalds 		right = packet[3] & 2;
1971da177e4SLinus Torvalds 		middle = packet[3] & 4;
1981da177e4SLinus Torvalds 		x = packet[1] | ((packet[2] & 0x78) << (7 - 3));
1991da177e4SLinus Torvalds 		y = packet[4] | ((packet[3] & 0x70) << (7 - 4));
2001da177e4SLinus Torvalds 		z = packet[5];
2011da177e4SLinus Torvalds 	}
2021da177e4SLinus Torvalds 
20399df65e7SKevin Cernekee 	if (priv->flags & ALPS_FW_BK_1) {
2043c00bb96SLaszlo Kajan 		back = packet[0] & 0x10;
2053c00bb96SLaszlo Kajan 		forward = packet[2] & 4;
206c30b4c10SIvan Casado Ruiz 	}
207c30b4c10SIvan Casado Ruiz 
20899df65e7SKevin Cernekee 	if (priv->flags & ALPS_FW_BK_2) {
209c30b4c10SIvan Casado Ruiz 		back = packet[3] & 4;
210c30b4c10SIvan Casado Ruiz 		forward = packet[2] & 4;
211c30b4c10SIvan Casado Ruiz 		if ((middle = forward && back))
212c30b4c10SIvan Casado Ruiz 			forward = back = 0;
213c30b4c10SIvan Casado Ruiz 	}
214c30b4c10SIvan Casado Ruiz 
2151da177e4SLinus Torvalds 	ges = packet[2] & 1;
2161da177e4SLinus Torvalds 	fin = packet[2] & 2;
2171da177e4SLinus Torvalds 
21899df65e7SKevin Cernekee 	if ((priv->flags & ALPS_DUALPOINT) && z == 127) {
2191da177e4SLinus Torvalds 		input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));
2201da177e4SLinus Torvalds 		input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
221d7ed5d88SUlrich Dangel 
2221d9f2626SSebastian Kapfer 		alps_report_buttons(psmouse, dev2, dev, left, right, middle);
223d7ed5d88SUlrich Dangel 
2241da177e4SLinus Torvalds 		input_sync(dev2);
2251da177e4SLinus Torvalds 		return;
2261da177e4SLinus Torvalds 	}
2271da177e4SLinus Torvalds 
2281d9f2626SSebastian Kapfer 	alps_report_buttons(psmouse, dev, dev2, left, right, middle);
229d7ed5d88SUlrich Dangel 
2301da177e4SLinus Torvalds 	/* Convert hardware tap to a reasonable Z value */
23171bb21b6SMaxim Levitsky 	if (ges && !fin)
23271bb21b6SMaxim Levitsky 		z = 40;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	/*
2351da177e4SLinus Torvalds 	 * A "tap and drag" operation is reported by the hardware as a transition
2361da177e4SLinus Torvalds 	 * from (!fin && ges) to (fin && ges). This should be translated to the
2371da177e4SLinus Torvalds 	 * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually.
2381da177e4SLinus Torvalds 	 */
2391da177e4SLinus Torvalds 	if (ges && fin && !priv->prev_fin) {
2401da177e4SLinus Torvalds 		input_report_abs(dev, ABS_X, x);
2411da177e4SLinus Torvalds 		input_report_abs(dev, ABS_Y, y);
2421da177e4SLinus Torvalds 		input_report_abs(dev, ABS_PRESSURE, 0);
2431da177e4SLinus Torvalds 		input_report_key(dev, BTN_TOOL_FINGER, 0);
2441da177e4SLinus Torvalds 		input_sync(dev);
2451da177e4SLinus Torvalds 	}
2461da177e4SLinus Torvalds 	priv->prev_fin = fin;
2471da177e4SLinus Torvalds 
24871bb21b6SMaxim Levitsky 	if (z > 30)
24971bb21b6SMaxim Levitsky 		input_report_key(dev, BTN_TOUCH, 1);
25071bb21b6SMaxim Levitsky 	if (z < 25)
25171bb21b6SMaxim Levitsky 		input_report_key(dev, BTN_TOUCH, 0);
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	if (z > 0) {
2541da177e4SLinus Torvalds 		input_report_abs(dev, ABS_X, x);
2551da177e4SLinus Torvalds 		input_report_abs(dev, ABS_Y, y);
2561da177e4SLinus Torvalds 	}
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	input_report_abs(dev, ABS_PRESSURE, z);
2591da177e4SLinus Torvalds 	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
2601da177e4SLinus Torvalds 
26199df65e7SKevin Cernekee 	if (priv->flags & ALPS_WHEEL)
262e6c047b9SVojtech Pavlik 		input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
2631da177e4SLinus Torvalds 
26499df65e7SKevin Cernekee 	if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
265c30b4c10SIvan Casado Ruiz 		input_report_key(dev, BTN_FORWARD, forward);
266c30b4c10SIvan Casado Ruiz 		input_report_key(dev, BTN_BACK, back);
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds 
26999df65e7SKevin Cernekee 	if (priv->flags & ALPS_FOUR_BUTTONS) {
27071bb21b6SMaxim Levitsky 		input_report_key(dev, BTN_0, packet[2] & 4);
27171bb21b6SMaxim Levitsky 		input_report_key(dev, BTN_1, packet[0] & 0x10);
27271bb21b6SMaxim Levitsky 		input_report_key(dev, BTN_2, packet[3] & 4);
27371bb21b6SMaxim Levitsky 		input_report_key(dev, BTN_3, packet[0] & 0x20);
27471bb21b6SMaxim Levitsky 	}
27571bb21b6SMaxim Levitsky 
2761da177e4SLinus Torvalds 	input_sync(dev);
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
27901ce661fSSeth Forshee /*
280ee65d4b3SYunkang Tang  * Process bitmap data for V5 protocols. Return value is null.
281ee65d4b3SYunkang Tang  *
282ee65d4b3SYunkang Tang  * The bitmaps don't have enough data to track fingers, so this function
283ee65d4b3SYunkang Tang  * only generates points representing a bounding box of at most two contacts.
284ee65d4b3SYunkang Tang  * These two points are returned in x1, y1, x2, and y2.
285ee65d4b3SYunkang Tang  */
286ee65d4b3SYunkang Tang static void alps_process_bitmap_dolphin(struct alps_data *priv,
287ee65d4b3SYunkang Tang 					struct alps_fields *fields,
288ee65d4b3SYunkang Tang 					int *x1, int *y1, int *x2, int *y2)
289ee65d4b3SYunkang Tang {
290ee65d4b3SYunkang Tang 	int box_middle_x, box_middle_y;
291ee65d4b3SYunkang Tang 	unsigned int x_map, y_map;
292ee65d4b3SYunkang Tang 	unsigned char start_bit, end_bit;
293ee65d4b3SYunkang Tang 	unsigned char x_msb, x_lsb, y_msb, y_lsb;
294ee65d4b3SYunkang Tang 
295ee65d4b3SYunkang Tang 	x_map = fields->x_map;
296ee65d4b3SYunkang Tang 	y_map = fields->y_map;
297ee65d4b3SYunkang Tang 
298ee65d4b3SYunkang Tang 	if (!x_map || !y_map)
299ee65d4b3SYunkang Tang 		return;
300ee65d4b3SYunkang Tang 
301ee65d4b3SYunkang Tang 	/* Get Most-significant and Least-significant bit */
302ee65d4b3SYunkang Tang 	x_msb = fls(x_map);
303ee65d4b3SYunkang Tang 	x_lsb = ffs(x_map);
304ee65d4b3SYunkang Tang 	y_msb = fls(y_map);
305ee65d4b3SYunkang Tang 	y_lsb = ffs(y_map);
306ee65d4b3SYunkang Tang 
307ee65d4b3SYunkang Tang 	/* Most-significant bit should never exceed max sensor line number */
308ee65d4b3SYunkang Tang 	if (x_msb > priv->x_bits || y_msb > priv->y_bits)
309ee65d4b3SYunkang Tang 		return;
310ee65d4b3SYunkang Tang 
311ee65d4b3SYunkang Tang 	*x1 = *y1 = *x2 = *y2 = 0;
312ee65d4b3SYunkang Tang 
313ee65d4b3SYunkang Tang 	if (fields->fingers > 1) {
314ee65d4b3SYunkang Tang 		start_bit = priv->x_bits - x_msb;
315ee65d4b3SYunkang Tang 		end_bit = priv->x_bits - x_lsb;
316ee65d4b3SYunkang Tang 		box_middle_x = (priv->x_max * (start_bit + end_bit)) /
317ee65d4b3SYunkang Tang 				(2 * (priv->x_bits - 1));
318ee65d4b3SYunkang Tang 
319ee65d4b3SYunkang Tang 		start_bit = y_lsb - 1;
320ee65d4b3SYunkang Tang 		end_bit = y_msb - 1;
321ee65d4b3SYunkang Tang 		box_middle_y = (priv->y_max * (start_bit + end_bit)) /
322ee65d4b3SYunkang Tang 				(2 * (priv->y_bits - 1));
323ee65d4b3SYunkang Tang 		*x1 = fields->x;
324ee65d4b3SYunkang Tang 		*y1 = fields->y;
325ee65d4b3SYunkang Tang 		*x2 = 2 * box_middle_x - *x1;
326ee65d4b3SYunkang Tang 		*y2 = 2 * box_middle_y - *y1;
327ee65d4b3SYunkang Tang 	}
328ee65d4b3SYunkang Tang }
329ee65d4b3SYunkang Tang 
330ee65d4b3SYunkang Tang /*
33101ce661fSSeth Forshee  * Process bitmap data from v3 and v4 protocols. Returns the number of
33201ce661fSSeth Forshee  * fingers detected. A return value of 0 means at least one of the
33301ce661fSSeth Forshee  * bitmaps was empty.
33401ce661fSSeth Forshee  *
33501ce661fSSeth Forshee  * The bitmaps don't have enough data to track fingers, so this function
33601ce661fSSeth Forshee  * only generates points representing a bounding box of all contacts.
33701ce661fSSeth Forshee  * These points are returned in x1, y1, x2, and y2 when the return value
33801ce661fSSeth Forshee  * is greater than 0.
33901ce661fSSeth Forshee  */
3407a9f73e7SKevin Cernekee static int alps_process_bitmap(struct alps_data *priv,
3417a9f73e7SKevin Cernekee 			       unsigned int x_map, unsigned int y_map,
34201ce661fSSeth Forshee 			       int *x1, int *y1, int *x2, int *y2)
34301ce661fSSeth Forshee {
34401ce661fSSeth Forshee 	struct alps_bitmap_point {
34501ce661fSSeth Forshee 		int start_bit;
34601ce661fSSeth Forshee 		int num_bits;
34701ce661fSSeth Forshee 	};
34801ce661fSSeth Forshee 
34901ce661fSSeth Forshee 	int fingers_x = 0, fingers_y = 0, fingers;
35001ce661fSSeth Forshee 	int i, bit, prev_bit;
35101ce661fSSeth Forshee 	struct alps_bitmap_point x_low = {0,}, x_high = {0,};
35201ce661fSSeth Forshee 	struct alps_bitmap_point y_low = {0,}, y_high = {0,};
35301ce661fSSeth Forshee 	struct alps_bitmap_point *point;
35401ce661fSSeth Forshee 
35501ce661fSSeth Forshee 	if (!x_map || !y_map)
35601ce661fSSeth Forshee 		return 0;
35701ce661fSSeth Forshee 
35801ce661fSSeth Forshee 	*x1 = *y1 = *x2 = *y2 = 0;
35901ce661fSSeth Forshee 
36001ce661fSSeth Forshee 	prev_bit = 0;
36101ce661fSSeth Forshee 	point = &x_low;
36201ce661fSSeth Forshee 	for (i = 0; x_map != 0; i++, x_map >>= 1) {
36301ce661fSSeth Forshee 		bit = x_map & 1;
36401ce661fSSeth Forshee 		if (bit) {
36501ce661fSSeth Forshee 			if (!prev_bit) {
36601ce661fSSeth Forshee 				point->start_bit = i;
36701ce661fSSeth Forshee 				fingers_x++;
36801ce661fSSeth Forshee 			}
36901ce661fSSeth Forshee 			point->num_bits++;
37001ce661fSSeth Forshee 		} else {
37101ce661fSSeth Forshee 			if (prev_bit)
37201ce661fSSeth Forshee 				point = &x_high;
37301ce661fSSeth Forshee 			else
37401ce661fSSeth Forshee 				point->num_bits = 0;
37501ce661fSSeth Forshee 		}
37601ce661fSSeth Forshee 		prev_bit = bit;
37701ce661fSSeth Forshee 	}
37801ce661fSSeth Forshee 
37901ce661fSSeth Forshee 	/*
38001ce661fSSeth Forshee 	 * y bitmap is reversed for what we need (lower positions are in
38101ce661fSSeth Forshee 	 * higher bits), so we process from the top end.
38201ce661fSSeth Forshee 	 */
3837a9f73e7SKevin Cernekee 	y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits);
38401ce661fSSeth Forshee 	prev_bit = 0;
38501ce661fSSeth Forshee 	point = &y_low;
38601ce661fSSeth Forshee 	for (i = 0; y_map != 0; i++, y_map <<= 1) {
38701ce661fSSeth Forshee 		bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1));
38801ce661fSSeth Forshee 		if (bit) {
38901ce661fSSeth Forshee 			if (!prev_bit) {
39001ce661fSSeth Forshee 				point->start_bit = i;
39101ce661fSSeth Forshee 				fingers_y++;
39201ce661fSSeth Forshee 			}
39301ce661fSSeth Forshee 			point->num_bits++;
39401ce661fSSeth Forshee 		} else {
39501ce661fSSeth Forshee 			if (prev_bit)
39601ce661fSSeth Forshee 				point = &y_high;
39701ce661fSSeth Forshee 			else
39801ce661fSSeth Forshee 				point->num_bits = 0;
39901ce661fSSeth Forshee 		}
40001ce661fSSeth Forshee 		prev_bit = bit;
40101ce661fSSeth Forshee 	}
40201ce661fSSeth Forshee 
40301ce661fSSeth Forshee 	/*
40401ce661fSSeth Forshee 	 * Fingers can overlap, so we use the maximum count of fingers
40501ce661fSSeth Forshee 	 * on either axis as the finger count.
40601ce661fSSeth Forshee 	 */
40701ce661fSSeth Forshee 	fingers = max(fingers_x, fingers_y);
40801ce661fSSeth Forshee 
40901ce661fSSeth Forshee 	/*
41020bea68bSHans de Goede 	 * If an axis reports only a single contact, we have overlapping or
41120bea68bSHans de Goede 	 * adjacent fingers. Divide the single contact between the two points.
41201ce661fSSeth Forshee 	 */
41301ce661fSSeth Forshee 	if (fingers_x == 1) {
41401ce661fSSeth Forshee 		i = x_low.num_bits / 2;
41501ce661fSSeth Forshee 		x_low.num_bits = x_low.num_bits - i;
41601ce661fSSeth Forshee 		x_high.start_bit = x_low.start_bit + i;
41701ce661fSSeth Forshee 		x_high.num_bits = max(i, 1);
41820bea68bSHans de Goede 	}
41920bea68bSHans de Goede 	if (fingers_y == 1) {
42001ce661fSSeth Forshee 		i = y_low.num_bits / 2;
42101ce661fSSeth Forshee 		y_low.num_bits = y_low.num_bits - i;
42201ce661fSSeth Forshee 		y_high.start_bit = y_low.start_bit + i;
42301ce661fSSeth Forshee 		y_high.num_bits = max(i, 1);
42401ce661fSSeth Forshee 	}
42501ce661fSSeth Forshee 
4267a9f73e7SKevin Cernekee 	*x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
4277a9f73e7SKevin Cernekee 	      (2 * (priv->x_bits - 1));
4287a9f73e7SKevin Cernekee 	*y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
4297a9f73e7SKevin Cernekee 	      (2 * (priv->y_bits - 1));
43001ce661fSSeth Forshee 
4317a9f73e7SKevin Cernekee 	*x2 = (priv->x_max *
4327a9f73e7SKevin Cernekee 	       (2 * x_high.start_bit + x_high.num_bits - 1)) /
4337a9f73e7SKevin Cernekee 	      (2 * (priv->x_bits - 1));
4347a9f73e7SKevin Cernekee 	*y2 = (priv->y_max *
4357a9f73e7SKevin Cernekee 	       (2 * y_high.start_bit + y_high.num_bits - 1)) /
4367a9f73e7SKevin Cernekee 	      (2 * (priv->y_bits - 1));
43701ce661fSSeth Forshee 
43801ce661fSSeth Forshee 	return fingers;
43901ce661fSSeth Forshee }
44001ce661fSSeth Forshee 
44101ce661fSSeth Forshee static void alps_set_slot(struct input_dev *dev, int slot, bool active,
44201ce661fSSeth Forshee 			  int x, int y)
44301ce661fSSeth Forshee {
44401ce661fSSeth Forshee 	input_mt_slot(dev, slot);
44501ce661fSSeth Forshee 	input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
44601ce661fSSeth Forshee 	if (active) {
44701ce661fSSeth Forshee 		input_report_abs(dev, ABS_MT_POSITION_X, x);
44801ce661fSSeth Forshee 		input_report_abs(dev, ABS_MT_POSITION_Y, y);
44901ce661fSSeth Forshee 	}
45001ce661fSSeth Forshee }
45101ce661fSSeth Forshee 
45201ce661fSSeth Forshee static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
45301ce661fSSeth Forshee 				     int x1, int y1, int x2, int y2)
45401ce661fSSeth Forshee {
45501ce661fSSeth Forshee 	alps_set_slot(dev, 0, num_fingers != 0, x1, y1);
45601ce661fSSeth Forshee 	alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
45701ce661fSSeth Forshee }
45801ce661fSSeth Forshee 
45925bded7cSSeth Forshee static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
46025bded7cSSeth Forshee {
46125bded7cSSeth Forshee 	struct alps_data *priv = psmouse->private;
46225bded7cSSeth Forshee 	unsigned char *packet = psmouse->packet;
46325bded7cSSeth Forshee 	struct input_dev *dev = priv->dev2;
46425bded7cSSeth Forshee 	int x, y, z, left, right, middle;
46525bded7cSSeth Forshee 
46625bded7cSSeth Forshee 	/* Sanity check packet */
46725bded7cSSeth Forshee 	if (!(packet[0] & 0x40)) {
46825bded7cSSeth Forshee 		psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n");
46925bded7cSSeth Forshee 		return;
47025bded7cSSeth Forshee 	}
47125bded7cSSeth Forshee 
47225bded7cSSeth Forshee 	/*
47325bded7cSSeth Forshee 	 * There's a special packet that seems to indicate the end
47425bded7cSSeth Forshee 	 * of a stream of trackstick data. Filter these out.
47525bded7cSSeth Forshee 	 */
47625bded7cSSeth Forshee 	if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f)
47725bded7cSSeth Forshee 		return;
47825bded7cSSeth Forshee 
47925bded7cSSeth Forshee 	x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f));
48025bded7cSSeth Forshee 	y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f));
48125bded7cSSeth Forshee 	z = (packet[4] & 0x7c) >> 2;
48225bded7cSSeth Forshee 
48325bded7cSSeth Forshee 	/*
48425bded7cSSeth Forshee 	 * The x and y values tend to be quite large, and when used
48525bded7cSSeth Forshee 	 * alone the trackstick is difficult to use. Scale them down
48625bded7cSSeth Forshee 	 * to compensate.
48725bded7cSSeth Forshee 	 */
48825bded7cSSeth Forshee 	x /= 8;
48925bded7cSSeth Forshee 	y /= 8;
49025bded7cSSeth Forshee 
49125bded7cSSeth Forshee 	input_report_rel(dev, REL_X, x);
49225bded7cSSeth Forshee 	input_report_rel(dev, REL_Y, -y);
49325bded7cSSeth Forshee 
49425bded7cSSeth Forshee 	/*
49525bded7cSSeth Forshee 	 * Most ALPS models report the trackstick buttons in the touchpad
49625bded7cSSeth Forshee 	 * packets, but a few report them here. No reliable way has been
49725bded7cSSeth Forshee 	 * found to differentiate between the models upfront, so we enable
49825bded7cSSeth Forshee 	 * the quirk in response to seeing a button press in the trackstick
49925bded7cSSeth Forshee 	 * packet.
50025bded7cSSeth Forshee 	 */
50125bded7cSSeth Forshee 	left = packet[3] & 0x01;
50225bded7cSSeth Forshee 	right = packet[3] & 0x02;
50325bded7cSSeth Forshee 	middle = packet[3] & 0x04;
50425bded7cSSeth Forshee 
50525bded7cSSeth Forshee 	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) &&
50625bded7cSSeth Forshee 	    (left || right || middle))
50725bded7cSSeth Forshee 		priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS;
50825bded7cSSeth Forshee 
50925bded7cSSeth Forshee 	if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) {
51025bded7cSSeth Forshee 		input_report_key(dev, BTN_LEFT, left);
51125bded7cSSeth Forshee 		input_report_key(dev, BTN_RIGHT, right);
51225bded7cSSeth Forshee 		input_report_key(dev, BTN_MIDDLE, middle);
51325bded7cSSeth Forshee 	}
51425bded7cSSeth Forshee 
51525bded7cSSeth Forshee 	input_sync(dev);
51625bded7cSSeth Forshee 	return;
51725bded7cSSeth Forshee }
51825bded7cSSeth Forshee 
519f85e5001SKevin Cernekee static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
520f85e5001SKevin Cernekee {
521f85e5001SKevin Cernekee 	f->left = !!(p[3] & 0x01);
522f85e5001SKevin Cernekee 	f->right = !!(p[3] & 0x02);
523f85e5001SKevin Cernekee 	f->middle = !!(p[3] & 0x04);
524f85e5001SKevin Cernekee 
525f85e5001SKevin Cernekee 	f->ts_left = !!(p[3] & 0x10);
526f85e5001SKevin Cernekee 	f->ts_right = !!(p[3] & 0x20);
527f85e5001SKevin Cernekee 	f->ts_middle = !!(p[3] & 0x40);
528f85e5001SKevin Cernekee }
529f85e5001SKevin Cernekee 
530ee65d4b3SYunkang Tang static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
531ee65d4b3SYunkang Tang 				 struct psmouse *psmouse)
532f85e5001SKevin Cernekee {
533f85e5001SKevin Cernekee 	f->first_mp = !!(p[4] & 0x40);
534f85e5001SKevin Cernekee 	f->is_mp = !!(p[0] & 0x40);
535f85e5001SKevin Cernekee 
536f85e5001SKevin Cernekee 	f->fingers = (p[5] & 0x3) + 1;
537f85e5001SKevin Cernekee 	f->x_map = ((p[4] & 0x7e) << 8) |
538f85e5001SKevin Cernekee 		   ((p[1] & 0x7f) << 2) |
539f85e5001SKevin Cernekee 		   ((p[0] & 0x30) >> 4);
540f85e5001SKevin Cernekee 	f->y_map = ((p[3] & 0x70) << 4) |
541f85e5001SKevin Cernekee 		   ((p[2] & 0x7f) << 1) |
542f85e5001SKevin Cernekee 		   (p[4] & 0x01);
543f85e5001SKevin Cernekee 
544f85e5001SKevin Cernekee 	f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
545f85e5001SKevin Cernekee 	       ((p[0] & 0x30) >> 4);
546f85e5001SKevin Cernekee 	f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
547f85e5001SKevin Cernekee 	f->z = p[5] & 0x7f;
548f85e5001SKevin Cernekee 
549f85e5001SKevin Cernekee 	alps_decode_buttons_v3(f, p);
550f85e5001SKevin Cernekee }
551f85e5001SKevin Cernekee 
552ee65d4b3SYunkang Tang static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
553ee65d4b3SYunkang Tang 				 struct psmouse *psmouse)
5541302bac3SKevin Cernekee {
555ee65d4b3SYunkang Tang 	alps_decode_pinnacle(f, p, psmouse);
5561302bac3SKevin Cernekee 
557f105e34aSYunkang Tang 	/* Rushmore's packet decode has a bit difference with Pinnacle's */
558f105e34aSYunkang Tang 	f->is_mp = !!(p[5] & 0x40);
559f105e34aSYunkang Tang 	f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
5601302bac3SKevin Cernekee 	f->x_map |= (p[5] & 0x10) << 11;
5611302bac3SKevin Cernekee 	f->y_map |= (p[5] & 0x20) << 6;
5621302bac3SKevin Cernekee }
5631302bac3SKevin Cernekee 
564ee65d4b3SYunkang Tang static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
565ee65d4b3SYunkang Tang 				struct psmouse *psmouse)
56675af9e56SDave Turvene {
567ee65d4b3SYunkang Tang 	u64 palm_data = 0;
568ee65d4b3SYunkang Tang 	struct alps_data *priv = psmouse->private;
569ee65d4b3SYunkang Tang 
57075af9e56SDave Turvene 	f->first_mp = !!(p[0] & 0x02);
57175af9e56SDave Turvene 	f->is_mp = !!(p[0] & 0x20);
57275af9e56SDave Turvene 
573ee65d4b3SYunkang Tang 	if (!f->is_mp) {
57475af9e56SDave Turvene 		f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
57575af9e56SDave Turvene 		f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
57675af9e56SDave Turvene 		f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
57775af9e56SDave Turvene 		alps_decode_buttons_v3(f, p);
578ee65d4b3SYunkang Tang 	} else {
579ee65d4b3SYunkang Tang 		f->fingers = ((p[0] & 0x6) >> 1 |
580ee65d4b3SYunkang Tang 		     (p[0] & 0x10) >> 2);
581ee65d4b3SYunkang Tang 
582ee65d4b3SYunkang Tang 		palm_data = (p[1] & 0x7f) |
583ee65d4b3SYunkang Tang 			    ((p[2] & 0x7f) << 7) |
584ee65d4b3SYunkang Tang 			    ((p[4] & 0x7f) << 14) |
585ee65d4b3SYunkang Tang 			    ((p[5] & 0x7f) << 21) |
586ee65d4b3SYunkang Tang 			    ((p[3] & 0x07) << 28) |
587ee65d4b3SYunkang Tang 			    (((u64)p[3] & 0x70) << 27) |
588ee65d4b3SYunkang Tang 			    (((u64)p[0] & 0x01) << 34);
589ee65d4b3SYunkang Tang 
590ee65d4b3SYunkang Tang 		/* Y-profile is stored in P(0) to p(n-1), n = y_bits; */
591ee65d4b3SYunkang Tang 		f->y_map = palm_data & (BIT(priv->y_bits) - 1);
592ee65d4b3SYunkang Tang 
593ee65d4b3SYunkang Tang 		/* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */
594ee65d4b3SYunkang Tang 		f->x_map = (palm_data >> priv->y_bits) &
595ee65d4b3SYunkang Tang 			   (BIT(priv->x_bits) - 1);
596ee65d4b3SYunkang Tang 	}
59775af9e56SDave Turvene }
59875af9e56SDave Turvene 
599ee65d4b3SYunkang Tang static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
60025bded7cSSeth Forshee {
60125bded7cSSeth Forshee 	struct alps_data *priv = psmouse->private;
60225bded7cSSeth Forshee 	unsigned char *packet = psmouse->packet;
60325bded7cSSeth Forshee 	struct input_dev *dev = psmouse->dev;
60425bded7cSSeth Forshee 	struct input_dev *dev2 = priv->dev2;
60520bea68bSHans de Goede 	int x1 = 0, y1 = 0, x2 = 0, y2 = 0, fingers = 0;
606ee65d4b3SYunkang Tang 	struct alps_fields f = {0};
607f85e5001SKevin Cernekee 
608ee65d4b3SYunkang Tang 	priv->decode_fields(&f, packet, psmouse);
60925bded7cSSeth Forshee 
61025bded7cSSeth Forshee 	/*
61101ce661fSSeth Forshee 	 * There's no single feature of touchpad position and bitmap packets
61201ce661fSSeth Forshee 	 * that can be used to distinguish between them. We rely on the fact
61301ce661fSSeth Forshee 	 * that a bitmap packet should always follow a position packet with
61401ce661fSSeth Forshee 	 * bit 6 of packet[4] set.
61525bded7cSSeth Forshee 	 */
61625bded7cSSeth Forshee 	if (priv->multi_packet) {
61725bded7cSSeth Forshee 		/*
61825bded7cSSeth Forshee 		 * Sometimes a position packet will indicate a multi-packet
61925bded7cSSeth Forshee 		 * sequence, but then what follows is another position
62025bded7cSSeth Forshee 		 * packet. Check for this, and when it happens process the
62125bded7cSSeth Forshee 		 * position packet as usual.
62225bded7cSSeth Forshee 		 */
623f85e5001SKevin Cernekee 		if (f.is_mp) {
624f85e5001SKevin Cernekee 			fingers = f.fingers;
625ee65d4b3SYunkang Tang 			if (priv->proto_version == ALPS_PROTO_V3) {
62620bea68bSHans de Goede 				if (alps_process_bitmap(priv, f.x_map,
627ee65d4b3SYunkang Tang 							f.y_map, &x1, &y1,
62820bea68bSHans de Goede 							&x2, &y2) == 0)
62920bea68bSHans de Goede 					fingers = 0; /* Use st data */
63001ce661fSSeth Forshee 
63101ce661fSSeth Forshee 				/* Now process position packet */
632ee65d4b3SYunkang Tang 				priv->decode_fields(&f, priv->multi_data,
633ee65d4b3SYunkang Tang 						    psmouse);
634ee65d4b3SYunkang Tang 			} else {
635ee65d4b3SYunkang Tang 				/*
636ee65d4b3SYunkang Tang 				 * Because Dolphin uses position packet's
637ee65d4b3SYunkang Tang 				 * coordinate data as Pt1 and uses it to
638ee65d4b3SYunkang Tang 				 * calculate Pt2, so we need to do position
639ee65d4b3SYunkang Tang 				 * packet decode first.
640ee65d4b3SYunkang Tang 				 */
641ee65d4b3SYunkang Tang 				priv->decode_fields(&f, priv->multi_data,
642ee65d4b3SYunkang Tang 						    psmouse);
643ee65d4b3SYunkang Tang 
644ee65d4b3SYunkang Tang 				/*
645ee65d4b3SYunkang Tang 				 * Since Dolphin's finger number is reliable,
646ee65d4b3SYunkang Tang 				 * there is no need to compare with bmap_fn.
647ee65d4b3SYunkang Tang 				 */
648ee65d4b3SYunkang Tang 				alps_process_bitmap_dolphin(priv, &f, &x1, &y1,
649ee65d4b3SYunkang Tang 							    &x2, &y2);
650ee65d4b3SYunkang Tang 			}
65101ce661fSSeth Forshee 		} else {
65201ce661fSSeth Forshee 			priv->multi_packet = 0;
65325bded7cSSeth Forshee 		}
65425bded7cSSeth Forshee 	}
65525bded7cSSeth Forshee 
65601ce661fSSeth Forshee 	/*
65701ce661fSSeth Forshee 	 * Bit 6 of byte 0 is not usually set in position packets. The only
65801ce661fSSeth Forshee 	 * times it seems to be set is in situations where the data is
65901ce661fSSeth Forshee 	 * suspect anyway, e.g. a palm resting flat on the touchpad. Given
66001ce661fSSeth Forshee 	 * this combined with the fact that this bit is useful for filtering
66101ce661fSSeth Forshee 	 * out misidentified bitmap packets, we reject anything with this
66201ce661fSSeth Forshee 	 * bit set.
66301ce661fSSeth Forshee 	 */
664f85e5001SKevin Cernekee 	if (f.is_mp)
66501ce661fSSeth Forshee 		return;
66601ce661fSSeth Forshee 
667f85e5001SKevin Cernekee 	if (!priv->multi_packet && f.first_mp) {
66825bded7cSSeth Forshee 		priv->multi_packet = 1;
66901ce661fSSeth Forshee 		memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
67001ce661fSSeth Forshee 		return;
67101ce661fSSeth Forshee 	}
67201ce661fSSeth Forshee 
67325bded7cSSeth Forshee 	priv->multi_packet = 0;
67425bded7cSSeth Forshee 
67525bded7cSSeth Forshee 	/*
67625bded7cSSeth Forshee 	 * Sometimes the hardware sends a single packet with z = 0
67725bded7cSSeth Forshee 	 * in the middle of a stream. Real releases generate packets
67825bded7cSSeth Forshee 	 * with x, y, and z all zero, so these seem to be flukes.
67925bded7cSSeth Forshee 	 * Ignore them.
68025bded7cSSeth Forshee 	 */
681f85e5001SKevin Cernekee 	if (f.x && f.y && !f.z)
68225bded7cSSeth Forshee 		return;
68325bded7cSSeth Forshee 
68401ce661fSSeth Forshee 	/*
68501ce661fSSeth Forshee 	 * If we don't have MT data or the bitmaps were empty, we have
68601ce661fSSeth Forshee 	 * to rely on ST data.
68701ce661fSSeth Forshee 	 */
68801ce661fSSeth Forshee 	if (!fingers) {
689f85e5001SKevin Cernekee 		x1 = f.x;
690f85e5001SKevin Cernekee 		y1 = f.y;
691f85e5001SKevin Cernekee 		fingers = f.z > 0 ? 1 : 0;
69201ce661fSSeth Forshee 	}
69301ce661fSSeth Forshee 
694f85e5001SKevin Cernekee 	if (f.z >= 64)
69525bded7cSSeth Forshee 		input_report_key(dev, BTN_TOUCH, 1);
69625bded7cSSeth Forshee 	else
69725bded7cSSeth Forshee 		input_report_key(dev, BTN_TOUCH, 0);
69825bded7cSSeth Forshee 
69901ce661fSSeth Forshee 	alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
70001ce661fSSeth Forshee 
701616575c2SDmitry Torokhov 	input_mt_report_finger_count(dev, fingers);
70201ce661fSSeth Forshee 
703f85e5001SKevin Cernekee 	input_report_key(dev, BTN_LEFT, f.left);
704f85e5001SKevin Cernekee 	input_report_key(dev, BTN_RIGHT, f.right);
705f85e5001SKevin Cernekee 	input_report_key(dev, BTN_MIDDLE, f.middle);
70601ce661fSSeth Forshee 
707f85e5001SKevin Cernekee 	if (f.z > 0) {
708f85e5001SKevin Cernekee 		input_report_abs(dev, ABS_X, f.x);
709f85e5001SKevin Cernekee 		input_report_abs(dev, ABS_Y, f.y);
71025bded7cSSeth Forshee 	}
711f85e5001SKevin Cernekee 	input_report_abs(dev, ABS_PRESSURE, f.z);
71225bded7cSSeth Forshee 
71325bded7cSSeth Forshee 	input_sync(dev);
71425bded7cSSeth Forshee 
71525bded7cSSeth Forshee 	if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
716f85e5001SKevin Cernekee 		input_report_key(dev2, BTN_LEFT, f.ts_left);
717f85e5001SKevin Cernekee 		input_report_key(dev2, BTN_RIGHT, f.ts_right);
718f85e5001SKevin Cernekee 		input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
71925bded7cSSeth Forshee 		input_sync(dev2);
72025bded7cSSeth Forshee 	}
72125bded7cSSeth Forshee }
72225bded7cSSeth Forshee 
72325bded7cSSeth Forshee static void alps_process_packet_v3(struct psmouse *psmouse)
72425bded7cSSeth Forshee {
72525bded7cSSeth Forshee 	unsigned char *packet = psmouse->packet;
72625bded7cSSeth Forshee 
72725bded7cSSeth Forshee 	/*
72825bded7cSSeth Forshee 	 * v3 protocol packets come in three types, two representing
72925bded7cSSeth Forshee 	 * touchpad data and one representing trackstick data.
73025bded7cSSeth Forshee 	 * Trackstick packets seem to be distinguished by always
73125bded7cSSeth Forshee 	 * having 0x3f in the last byte. This value has never been
73225bded7cSSeth Forshee 	 * observed in the last byte of either of the other types
73325bded7cSSeth Forshee 	 * of packets.
73425bded7cSSeth Forshee 	 */
73525bded7cSSeth Forshee 	if (packet[5] == 0x3f) {
73625bded7cSSeth Forshee 		alps_process_trackstick_packet_v3(psmouse);
73725bded7cSSeth Forshee 		return;
73825bded7cSSeth Forshee 	}
73925bded7cSSeth Forshee 
740ee65d4b3SYunkang Tang 	alps_process_touchpad_packet_v3_v5(psmouse);
74125bded7cSSeth Forshee }
74225bded7cSSeth Forshee 
74395f75e91SYunkang Tang static void alps_process_packet_v6(struct psmouse *psmouse)
74495f75e91SYunkang Tang {
74595f75e91SYunkang Tang 	struct alps_data *priv = psmouse->private;
74695f75e91SYunkang Tang 	unsigned char *packet = psmouse->packet;
74795f75e91SYunkang Tang 	struct input_dev *dev = psmouse->dev;
74895f75e91SYunkang Tang 	struct input_dev *dev2 = priv->dev2;
74995f75e91SYunkang Tang 	int x, y, z, left, right, middle;
75095f75e91SYunkang Tang 
75195f75e91SYunkang Tang 	/*
75295f75e91SYunkang Tang 	 * We can use Byte5 to distinguish if the packet is from Touchpad
75395f75e91SYunkang Tang 	 * or Trackpoint.
75495f75e91SYunkang Tang 	 * Touchpad:	0 - 0x7E
75595f75e91SYunkang Tang 	 * Trackpoint:	0x7F
75695f75e91SYunkang Tang 	 */
75795f75e91SYunkang Tang 	if (packet[5] == 0x7F) {
75895f75e91SYunkang Tang 		/* It should be a DualPoint when received Trackpoint packet */
75995f75e91SYunkang Tang 		if (!(priv->flags & ALPS_DUALPOINT))
76095f75e91SYunkang Tang 			return;
76195f75e91SYunkang Tang 
76295f75e91SYunkang Tang 		/* Trackpoint packet */
76395f75e91SYunkang Tang 		x = packet[1] | ((packet[3] & 0x20) << 2);
76495f75e91SYunkang Tang 		y = packet[2] | ((packet[3] & 0x40) << 1);
76595f75e91SYunkang Tang 		z = packet[4];
76695f75e91SYunkang Tang 		left = packet[3] & 0x01;
76795f75e91SYunkang Tang 		right = packet[3] & 0x02;
76895f75e91SYunkang Tang 		middle = packet[3] & 0x04;
76995f75e91SYunkang Tang 
77095f75e91SYunkang Tang 		/* To prevent the cursor jump when finger lifted */
77195f75e91SYunkang Tang 		if (x == 0x7F && y == 0x7F && z == 0x7F)
77295f75e91SYunkang Tang 			x = y = z = 0;
77395f75e91SYunkang Tang 
77495f75e91SYunkang Tang 		/* Divide 4 since trackpoint's speed is too fast */
77595f75e91SYunkang Tang 		input_report_rel(dev2, REL_X, (char)x / 4);
77695f75e91SYunkang Tang 		input_report_rel(dev2, REL_Y, -((char)y / 4));
77795f75e91SYunkang Tang 
77895f75e91SYunkang Tang 		input_report_key(dev2, BTN_LEFT, left);
77995f75e91SYunkang Tang 		input_report_key(dev2, BTN_RIGHT, right);
78095f75e91SYunkang Tang 		input_report_key(dev2, BTN_MIDDLE, middle);
78195f75e91SYunkang Tang 
78295f75e91SYunkang Tang 		input_sync(dev2);
78395f75e91SYunkang Tang 		return;
78495f75e91SYunkang Tang 	}
78595f75e91SYunkang Tang 
78695f75e91SYunkang Tang 	/* Touchpad packet */
78795f75e91SYunkang Tang 	x = packet[1] | ((packet[3] & 0x78) << 4);
78895f75e91SYunkang Tang 	y = packet[2] | ((packet[4] & 0x78) << 4);
78995f75e91SYunkang Tang 	z = packet[5];
79095f75e91SYunkang Tang 	left = packet[3] & 0x01;
79195f75e91SYunkang Tang 	right = packet[3] & 0x02;
79295f75e91SYunkang Tang 
79395f75e91SYunkang Tang 	if (z > 30)
79495f75e91SYunkang Tang 		input_report_key(dev, BTN_TOUCH, 1);
79595f75e91SYunkang Tang 	if (z < 25)
79695f75e91SYunkang Tang 		input_report_key(dev, BTN_TOUCH, 0);
79795f75e91SYunkang Tang 
79895f75e91SYunkang Tang 	if (z > 0) {
79995f75e91SYunkang Tang 		input_report_abs(dev, ABS_X, x);
80095f75e91SYunkang Tang 		input_report_abs(dev, ABS_Y, y);
80195f75e91SYunkang Tang 	}
80295f75e91SYunkang Tang 
80395f75e91SYunkang Tang 	input_report_abs(dev, ABS_PRESSURE, z);
80495f75e91SYunkang Tang 	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
80595f75e91SYunkang Tang 
80695f75e91SYunkang Tang 	/* v6 touchpad does not have middle button */
80795f75e91SYunkang Tang 	input_report_key(dev, BTN_LEFT, left);
80895f75e91SYunkang Tang 	input_report_key(dev, BTN_RIGHT, right);
80995f75e91SYunkang Tang 
81095f75e91SYunkang Tang 	input_sync(dev);
81195f75e91SYunkang Tang }
81295f75e91SYunkang Tang 
81325bded7cSSeth Forshee static void alps_process_packet_v4(struct psmouse *psmouse)
81425bded7cSSeth Forshee {
8153b7e09faSGeorge Pantalos 	struct alps_data *priv = psmouse->private;
81625bded7cSSeth Forshee 	unsigned char *packet = psmouse->packet;
81725bded7cSSeth Forshee 	struct input_dev *dev = psmouse->dev;
8183b7e09faSGeorge Pantalos 	int offset;
81925bded7cSSeth Forshee 	int x, y, z;
82025bded7cSSeth Forshee 	int left, right;
8213b7e09faSGeorge Pantalos 	int x1, y1, x2, y2;
8223b7e09faSGeorge Pantalos 	int fingers = 0;
8233b7e09faSGeorge Pantalos 	unsigned int x_bitmap, y_bitmap;
8243b7e09faSGeorge Pantalos 
8253b7e09faSGeorge Pantalos 	/*
8263b7e09faSGeorge Pantalos 	 * v4 has a 6-byte encoding for bitmap data, but this data is
8273b7e09faSGeorge Pantalos 	 * broken up between 3 normal packets. Use priv->multi_packet to
8283b7e09faSGeorge Pantalos 	 * track our position in the bitmap packet.
8293b7e09faSGeorge Pantalos 	 */
8303b7e09faSGeorge Pantalos 	if (packet[6] & 0x40) {
8313b7e09faSGeorge Pantalos 		/* sync, reset position */
8323b7e09faSGeorge Pantalos 		priv->multi_packet = 0;
8333b7e09faSGeorge Pantalos 	}
8343b7e09faSGeorge Pantalos 
8353b7e09faSGeorge Pantalos 	if (WARN_ON_ONCE(priv->multi_packet > 2))
8363b7e09faSGeorge Pantalos 		return;
8373b7e09faSGeorge Pantalos 
8383b7e09faSGeorge Pantalos 	offset = 2 * priv->multi_packet;
8393b7e09faSGeorge Pantalos 	priv->multi_data[offset] = packet[6];
8403b7e09faSGeorge Pantalos 	priv->multi_data[offset + 1] = packet[7];
8413b7e09faSGeorge Pantalos 
8423b7e09faSGeorge Pantalos 	if (++priv->multi_packet > 2) {
8433b7e09faSGeorge Pantalos 		priv->multi_packet = 0;
8443b7e09faSGeorge Pantalos 
8453b7e09faSGeorge Pantalos 		x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) |
8463b7e09faSGeorge Pantalos 			   ((priv->multi_data[3] & 0x60) << 3) |
8473b7e09faSGeorge Pantalos 			   ((priv->multi_data[0] & 0x3f) << 2) |
8483b7e09faSGeorge Pantalos 			   ((priv->multi_data[1] & 0x60) >> 5);
8493b7e09faSGeorge Pantalos 		y_bitmap = ((priv->multi_data[5] & 0x01) << 10) |
8503b7e09faSGeorge Pantalos 			   ((priv->multi_data[3] & 0x1f) << 5) |
8513b7e09faSGeorge Pantalos 			    (priv->multi_data[1] & 0x1f);
8523b7e09faSGeorge Pantalos 
8537a9f73e7SKevin Cernekee 		fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap,
8543b7e09faSGeorge Pantalos 					      &x1, &y1, &x2, &y2);
8553b7e09faSGeorge Pantalos 
8563b7e09faSGeorge Pantalos 		/* Store MT data.*/
8573b7e09faSGeorge Pantalos 		priv->fingers = fingers;
8583b7e09faSGeorge Pantalos 		priv->x1 = x1;
8593b7e09faSGeorge Pantalos 		priv->x2 = x2;
8603b7e09faSGeorge Pantalos 		priv->y1 = y1;
8613b7e09faSGeorge Pantalos 		priv->y2 = y2;
8623b7e09faSGeorge Pantalos 	}
86325bded7cSSeth Forshee 
86425bded7cSSeth Forshee 	left = packet[4] & 0x01;
86525bded7cSSeth Forshee 	right = packet[4] & 0x02;
86625bded7cSSeth Forshee 
86725bded7cSSeth Forshee 	x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
86825bded7cSSeth Forshee 	    ((packet[0] & 0x30) >> 4);
86925bded7cSSeth Forshee 	y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
87025bded7cSSeth Forshee 	z = packet[5] & 0x7f;
87125bded7cSSeth Forshee 
8723b7e09faSGeorge Pantalos 	/*
8733b7e09faSGeorge Pantalos 	 * If there were no contacts in the bitmap, use ST
8743b7e09faSGeorge Pantalos 	 * points in MT reports.
8753b7e09faSGeorge Pantalos 	 * If there were two contacts or more, report MT data.
8763b7e09faSGeorge Pantalos 	 */
8773b7e09faSGeorge Pantalos 	if (priv->fingers < 2) {
8783b7e09faSGeorge Pantalos 		x1 = x;
8793b7e09faSGeorge Pantalos 		y1 = y;
8803b7e09faSGeorge Pantalos 		fingers = z > 0 ? 1 : 0;
8813b7e09faSGeorge Pantalos 	} else {
8823b7e09faSGeorge Pantalos 		fingers = priv->fingers;
8833b7e09faSGeorge Pantalos 		x1 = priv->x1;
8843b7e09faSGeorge Pantalos 		x2 = priv->x2;
8853b7e09faSGeorge Pantalos 		y1 = priv->y1;
8863b7e09faSGeorge Pantalos 		y2 = priv->y2;
8873b7e09faSGeorge Pantalos 	}
8883b7e09faSGeorge Pantalos 
88925bded7cSSeth Forshee 	if (z >= 64)
89025bded7cSSeth Forshee 		input_report_key(dev, BTN_TOUCH, 1);
89125bded7cSSeth Forshee 	else
89225bded7cSSeth Forshee 		input_report_key(dev, BTN_TOUCH, 0);
89325bded7cSSeth Forshee 
8943b7e09faSGeorge Pantalos 	alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
8953b7e09faSGeorge Pantalos 
896616575c2SDmitry Torokhov 	input_mt_report_finger_count(dev, fingers);
8973b7e09faSGeorge Pantalos 
8983b7e09faSGeorge Pantalos 	input_report_key(dev, BTN_LEFT, left);
8993b7e09faSGeorge Pantalos 	input_report_key(dev, BTN_RIGHT, right);
9003b7e09faSGeorge Pantalos 
90125bded7cSSeth Forshee 	if (z > 0) {
90225bded7cSSeth Forshee 		input_report_abs(dev, ABS_X, x);
90325bded7cSSeth Forshee 		input_report_abs(dev, ABS_Y, y);
90425bded7cSSeth Forshee 	}
90525bded7cSSeth Forshee 	input_report_abs(dev, ABS_PRESSURE, z);
90625bded7cSSeth Forshee 
90725bded7cSSeth Forshee 	input_sync(dev);
90825bded7cSSeth Forshee }
90925bded7cSSeth Forshee 
9101d9f2626SSebastian Kapfer static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
9111d9f2626SSebastian Kapfer 					unsigned char packet[],
9121d9f2626SSebastian Kapfer 					bool report_buttons)
9131d9f2626SSebastian Kapfer {
9141d9f2626SSebastian Kapfer 	struct alps_data *priv = psmouse->private;
9151d9f2626SSebastian Kapfer 	struct input_dev *dev2 = priv->dev2;
9161d9f2626SSebastian Kapfer 
9171d9f2626SSebastian Kapfer 	if (report_buttons)
9181d9f2626SSebastian Kapfer 		alps_report_buttons(psmouse, dev2, psmouse->dev,
9191d9f2626SSebastian Kapfer 				packet[0] & 1, packet[0] & 2, packet[0] & 4);
9201d9f2626SSebastian Kapfer 
9211d9f2626SSebastian Kapfer 	input_report_rel(dev2, REL_X,
9221d9f2626SSebastian Kapfer 		packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
9231d9f2626SSebastian Kapfer 	input_report_rel(dev2, REL_Y,
9241d9f2626SSebastian Kapfer 		packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
9251d9f2626SSebastian Kapfer 
9261d9f2626SSebastian Kapfer 	input_sync(dev2);
9271d9f2626SSebastian Kapfer }
9281d9f2626SSebastian Kapfer 
9291d9f2626SSebastian Kapfer static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
9301da177e4SLinus Torvalds {
9311da177e4SLinus Torvalds 	struct alps_data *priv = psmouse->private;
9321da177e4SLinus Torvalds 
9331d9f2626SSebastian Kapfer 	if (psmouse->pktcnt < 6)
9341d9f2626SSebastian Kapfer 		return PSMOUSE_GOOD_DATA;
9351d9f2626SSebastian Kapfer 
9361d9f2626SSebastian Kapfer 	if (psmouse->pktcnt == 6) {
9371d9f2626SSebastian Kapfer 		/*
9381d9f2626SSebastian Kapfer 		 * Start a timer to flush the packet if it ends up last
9391d9f2626SSebastian Kapfer 		 * 6-byte packet in the stream. Timer needs to fire
9401d9f2626SSebastian Kapfer 		 * psmouse core times out itself. 20 ms should be enough
9411d9f2626SSebastian Kapfer 		 * to decide if we are getting more data or not.
9421d9f2626SSebastian Kapfer 		 */
9431d9f2626SSebastian Kapfer 		mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20));
9441d9f2626SSebastian Kapfer 		return PSMOUSE_GOOD_DATA;
9451d9f2626SSebastian Kapfer 	}
9461d9f2626SSebastian Kapfer 
9471d9f2626SSebastian Kapfer 	del_timer(&priv->timer);
9481d9f2626SSebastian Kapfer 
9491d9f2626SSebastian Kapfer 	if (psmouse->packet[6] & 0x80) {
9501d9f2626SSebastian Kapfer 
9511d9f2626SSebastian Kapfer 		/*
9521d9f2626SSebastian Kapfer 		 * Highest bit is set - that means we either had
9531d9f2626SSebastian Kapfer 		 * complete ALPS packet and this is start of the
9541d9f2626SSebastian Kapfer 		 * next packet or we got garbage.
9551d9f2626SSebastian Kapfer 		 */
9561d9f2626SSebastian Kapfer 
9571d9f2626SSebastian Kapfer 		if (((psmouse->packet[3] |
9581d9f2626SSebastian Kapfer 		      psmouse->packet[4] |
9591d9f2626SSebastian Kapfer 		      psmouse->packet[5]) & 0x80) ||
96099df65e7SKevin Cernekee 		    (!alps_is_valid_first_byte(priv, psmouse->packet[6]))) {
961b5d21704SDmitry Torokhov 			psmouse_dbg(psmouse,
9623b112923SAndy Shevchenko 				    "refusing packet %4ph (suspected interleaved ps/2)\n",
9633b112923SAndy Shevchenko 				    psmouse->packet + 3);
9641d9f2626SSebastian Kapfer 			return PSMOUSE_BAD_DATA;
9651d9f2626SSebastian Kapfer 		}
9661d9f2626SSebastian Kapfer 
96724af5cb9SKevin Cernekee 		priv->process_packet(psmouse);
9681d9f2626SSebastian Kapfer 
9691d9f2626SSebastian Kapfer 		/* Continue with the next packet */
9701d9f2626SSebastian Kapfer 		psmouse->packet[0] = psmouse->packet[6];
9711d9f2626SSebastian Kapfer 		psmouse->pktcnt = 1;
9721d9f2626SSebastian Kapfer 
9731d9f2626SSebastian Kapfer 	} else {
9741d9f2626SSebastian Kapfer 
9751d9f2626SSebastian Kapfer 		/*
9761d9f2626SSebastian Kapfer 		 * High bit is 0 - that means that we indeed got a PS/2
9771d9f2626SSebastian Kapfer 		 * packet in the middle of ALPS packet.
9781d9f2626SSebastian Kapfer 		 *
9791d9f2626SSebastian Kapfer 		 * There is also possibility that we got 6-byte ALPS
9801d9f2626SSebastian Kapfer 		 * packet followed  by 3-byte packet from trackpoint. We
9811d9f2626SSebastian Kapfer 		 * can not distinguish between these 2 scenarios but
982b5d21704SDmitry Torokhov 		 * because the latter is unlikely to happen in course of
9831d9f2626SSebastian Kapfer 		 * normal operation (user would need to press all
9841d9f2626SSebastian Kapfer 		 * buttons on the pad and start moving trackpoint
9851d9f2626SSebastian Kapfer 		 * without touching the pad surface) we assume former.
9861d9f2626SSebastian Kapfer 		 * Even if we are wrong the wost thing that would happen
9871d9f2626SSebastian Kapfer 		 * the cursor would jump but we should not get protocol
988b5d21704SDmitry Torokhov 		 * de-synchronization.
9891d9f2626SSebastian Kapfer 		 */
9901d9f2626SSebastian Kapfer 
9911d9f2626SSebastian Kapfer 		alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
9921d9f2626SSebastian Kapfer 					    false);
9931d9f2626SSebastian Kapfer 
9941d9f2626SSebastian Kapfer 		/*
9951d9f2626SSebastian Kapfer 		 * Continue with the standard ALPS protocol handling,
9961d9f2626SSebastian Kapfer 		 * but make sure we won't process it as an interleaved
9971d9f2626SSebastian Kapfer 		 * packet again, which may happen if all buttons are
9981d9f2626SSebastian Kapfer 		 * pressed. To avoid this let's reset the 4th bit which
9991d9f2626SSebastian Kapfer 		 * is normally 1.
10001d9f2626SSebastian Kapfer 		 */
10011d9f2626SSebastian Kapfer 		psmouse->packet[3] = psmouse->packet[6] & 0xf7;
10021d9f2626SSebastian Kapfer 		psmouse->pktcnt = 4;
10031d9f2626SSebastian Kapfer 	}
10041d9f2626SSebastian Kapfer 
10051d9f2626SSebastian Kapfer 	return PSMOUSE_GOOD_DATA;
10061d9f2626SSebastian Kapfer }
10071d9f2626SSebastian Kapfer 
10081d9f2626SSebastian Kapfer static void alps_flush_packet(unsigned long data)
10091d9f2626SSebastian Kapfer {
10101d9f2626SSebastian Kapfer 	struct psmouse *psmouse = (struct psmouse *)data;
101124af5cb9SKevin Cernekee 	struct alps_data *priv = psmouse->private;
10121d9f2626SSebastian Kapfer 
10131d9f2626SSebastian Kapfer 	serio_pause_rx(psmouse->ps2dev.serio);
10141d9f2626SSebastian Kapfer 
1015b46615feSSeth Forshee 	if (psmouse->pktcnt == psmouse->pktsize) {
10161d9f2626SSebastian Kapfer 
10171d9f2626SSebastian Kapfer 		/*
10181d9f2626SSebastian Kapfer 		 * We did not any more data in reasonable amount of time.
10191d9f2626SSebastian Kapfer 		 * Validate the last 3 bytes and process as a standard
10201d9f2626SSebastian Kapfer 		 * ALPS packet.
10211d9f2626SSebastian Kapfer 		 */
10221d9f2626SSebastian Kapfer 		if ((psmouse->packet[3] |
10231d9f2626SSebastian Kapfer 		     psmouse->packet[4] |
10241d9f2626SSebastian Kapfer 		     psmouse->packet[5]) & 0x80) {
1025b5d21704SDmitry Torokhov 			psmouse_dbg(psmouse,
10263b112923SAndy Shevchenko 				    "refusing packet %3ph (suspected interleaved ps/2)\n",
10273b112923SAndy Shevchenko 				    psmouse->packet + 3);
10281d9f2626SSebastian Kapfer 		} else {
102924af5cb9SKevin Cernekee 			priv->process_packet(psmouse);
10301d9f2626SSebastian Kapfer 		}
10311d9f2626SSebastian Kapfer 		psmouse->pktcnt = 0;
10321d9f2626SSebastian Kapfer 	}
10331d9f2626SSebastian Kapfer 
10341d9f2626SSebastian Kapfer 	serio_continue_rx(psmouse->ps2dev.serio);
10351d9f2626SSebastian Kapfer }
10361d9f2626SSebastian Kapfer 
10371d9f2626SSebastian Kapfer static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
10381d9f2626SSebastian Kapfer {
10391d9f2626SSebastian Kapfer 	struct alps_data *priv = psmouse->private;
10401d9f2626SSebastian Kapfer 
10411da177e4SLinus Torvalds 	if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
10421da177e4SLinus Torvalds 		if (psmouse->pktcnt == 3) {
10431d9f2626SSebastian Kapfer 			alps_report_bare_ps2_packet(psmouse, psmouse->packet,
10441d9f2626SSebastian Kapfer 						    true);
10451da177e4SLinus Torvalds 			return PSMOUSE_FULL_PACKET;
10461da177e4SLinus Torvalds 		}
10471da177e4SLinus Torvalds 		return PSMOUSE_GOOD_DATA;
10481da177e4SLinus Torvalds 	}
10491da177e4SLinus Torvalds 
10501d9f2626SSebastian Kapfer 	/* Check for PS/2 packet stuffed in the middle of ALPS packet. */
10511d9f2626SSebastian Kapfer 
105299df65e7SKevin Cernekee 	if ((priv->flags & ALPS_PS2_INTERLEAVED) &&
10531d9f2626SSebastian Kapfer 	    psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) {
10541d9f2626SSebastian Kapfer 		return alps_handle_interleaved_ps2(psmouse);
10551d9f2626SSebastian Kapfer 	}
10561d9f2626SSebastian Kapfer 
105799df65e7SKevin Cernekee 	if (!alps_is_valid_first_byte(priv, psmouse->packet[0])) {
1058b5d21704SDmitry Torokhov 		psmouse_dbg(psmouse,
1059b5d21704SDmitry Torokhov 			    "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
106099df65e7SKevin Cernekee 			    psmouse->packet[0], priv->mask0, priv->byte0);
10611da177e4SLinus Torvalds 		return PSMOUSE_BAD_DATA;
10621d9f2626SSebastian Kapfer 	}
10631da177e4SLinus Torvalds 
1064b46615feSSeth Forshee 	/* Bytes 2 - pktsize should have 0 in the highest bit */
106595f75e91SYunkang Tang 	if ((priv->proto_version < ALPS_PROTO_V5) &&
106675af9e56SDave Turvene 	    psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
10671d9f2626SSebastian Kapfer 	    (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
1068b5d21704SDmitry Torokhov 		psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
1069b5d21704SDmitry Torokhov 			    psmouse->pktcnt - 1,
1070b5d21704SDmitry Torokhov 			    psmouse->packet[psmouse->pktcnt - 1]);
10711da177e4SLinus Torvalds 		return PSMOUSE_BAD_DATA;
10721d9f2626SSebastian Kapfer 	}
10731da177e4SLinus Torvalds 
1074b46615feSSeth Forshee 	if (psmouse->pktcnt == psmouse->pktsize) {
107524af5cb9SKevin Cernekee 		priv->process_packet(psmouse);
10761da177e4SLinus Torvalds 		return PSMOUSE_FULL_PACKET;
10771da177e4SLinus Torvalds 	}
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 	return PSMOUSE_GOOD_DATA;
10801da177e4SLinus Torvalds }
10811da177e4SLinus Torvalds 
108225bded7cSSeth Forshee static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble)
108325bded7cSSeth Forshee {
108425bded7cSSeth Forshee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
108525bded7cSSeth Forshee 	struct alps_data *priv = psmouse->private;
108625bded7cSSeth Forshee 	int command;
108725bded7cSSeth Forshee 	unsigned char *param;
108825bded7cSSeth Forshee 	unsigned char dummy[4];
108925bded7cSSeth Forshee 
109025bded7cSSeth Forshee 	BUG_ON(nibble > 0xf);
109125bded7cSSeth Forshee 
109225bded7cSSeth Forshee 	command = priv->nibble_commands[nibble].command;
109325bded7cSSeth Forshee 	param = (command & 0x0f00) ?
109425bded7cSSeth Forshee 		dummy : (unsigned char *)&priv->nibble_commands[nibble].data;
109525bded7cSSeth Forshee 
109625bded7cSSeth Forshee 	if (ps2_command(ps2dev, param, command))
109725bded7cSSeth Forshee 		return -1;
109825bded7cSSeth Forshee 
109925bded7cSSeth Forshee 	return 0;
110025bded7cSSeth Forshee }
110125bded7cSSeth Forshee 
110225bded7cSSeth Forshee static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr)
110325bded7cSSeth Forshee {
110425bded7cSSeth Forshee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
110525bded7cSSeth Forshee 	struct alps_data *priv = psmouse->private;
110625bded7cSSeth Forshee 	int i, nibble;
110725bded7cSSeth Forshee 
110825bded7cSSeth Forshee 	if (ps2_command(ps2dev, NULL, priv->addr_command))
110925bded7cSSeth Forshee 		return -1;
111025bded7cSSeth Forshee 
111125bded7cSSeth Forshee 	for (i = 12; i >= 0; i -= 4) {
111225bded7cSSeth Forshee 		nibble = (addr >> i) & 0xf;
111325bded7cSSeth Forshee 		if (alps_command_mode_send_nibble(psmouse, nibble))
111425bded7cSSeth Forshee 			return -1;
111525bded7cSSeth Forshee 	}
111625bded7cSSeth Forshee 
111725bded7cSSeth Forshee 	return 0;
111825bded7cSSeth Forshee }
111925bded7cSSeth Forshee 
112025bded7cSSeth Forshee static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr)
112125bded7cSSeth Forshee {
112225bded7cSSeth Forshee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
112325bded7cSSeth Forshee 	unsigned char param[4];
112425bded7cSSeth Forshee 
112525bded7cSSeth Forshee 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
112625bded7cSSeth Forshee 		return -1;
112725bded7cSSeth Forshee 
112825bded7cSSeth Forshee 	/*
112925bded7cSSeth Forshee 	 * The address being read is returned in the first two bytes
113025bded7cSSeth Forshee 	 * of the result. Check that this address matches the expected
113125bded7cSSeth Forshee 	 * address.
113225bded7cSSeth Forshee 	 */
113325bded7cSSeth Forshee 	if (addr != ((param[0] << 8) | param[1]))
113425bded7cSSeth Forshee 		return -1;
113525bded7cSSeth Forshee 
113625bded7cSSeth Forshee 	return param[2];
113725bded7cSSeth Forshee }
113825bded7cSSeth Forshee 
113925bded7cSSeth Forshee static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr)
114025bded7cSSeth Forshee {
114125bded7cSSeth Forshee 	if (alps_command_mode_set_addr(psmouse, addr))
114225bded7cSSeth Forshee 		return -1;
114325bded7cSSeth Forshee 	return __alps_command_mode_read_reg(psmouse, addr);
114425bded7cSSeth Forshee }
114525bded7cSSeth Forshee 
114625bded7cSSeth Forshee static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value)
114725bded7cSSeth Forshee {
114825bded7cSSeth Forshee 	if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf))
114925bded7cSSeth Forshee 		return -1;
115025bded7cSSeth Forshee 	if (alps_command_mode_send_nibble(psmouse, value & 0xf))
115125bded7cSSeth Forshee 		return -1;
115225bded7cSSeth Forshee 	return 0;
115325bded7cSSeth Forshee }
115425bded7cSSeth Forshee 
115525bded7cSSeth Forshee static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr,
115625bded7cSSeth Forshee 				       u8 value)
115725bded7cSSeth Forshee {
115825bded7cSSeth Forshee 	if (alps_command_mode_set_addr(psmouse, addr))
115925bded7cSSeth Forshee 		return -1;
116025bded7cSSeth Forshee 	return __alps_command_mode_write_reg(psmouse, value);
116125bded7cSSeth Forshee }
116225bded7cSSeth Forshee 
116324ba9707SKevin Cernekee static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
116424ba9707SKevin Cernekee 			int repeated_command, unsigned char *param)
116524ba9707SKevin Cernekee {
116624ba9707SKevin Cernekee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
116724ba9707SKevin Cernekee 
116824ba9707SKevin Cernekee 	param[0] = 0;
116924ba9707SKevin Cernekee 	if (init_command && ps2_command(ps2dev, param, init_command))
117024ba9707SKevin Cernekee 		return -EIO;
117124ba9707SKevin Cernekee 
117224ba9707SKevin Cernekee 	if (ps2_command(ps2dev,  NULL, repeated_command) ||
117324ba9707SKevin Cernekee 	    ps2_command(ps2dev,  NULL, repeated_command) ||
117424ba9707SKevin Cernekee 	    ps2_command(ps2dev,  NULL, repeated_command))
117524ba9707SKevin Cernekee 		return -EIO;
117624ba9707SKevin Cernekee 
117724ba9707SKevin Cernekee 	param[0] = param[1] = param[2] = 0xff;
117824ba9707SKevin Cernekee 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
117924ba9707SKevin Cernekee 		return -EIO;
118024ba9707SKevin Cernekee 
118139fbe585SDmitry Torokhov 	psmouse_dbg(psmouse, "%2.2X report: %3ph\n",
118239fbe585SDmitry Torokhov 		    repeated_command, param);
118324ba9707SKevin Cernekee 	return 0;
118424ba9707SKevin Cernekee }
118524ba9707SKevin Cernekee 
1186d18e53fcSKevin Cernekee static int alps_enter_command_mode(struct psmouse *psmouse)
118725bded7cSSeth Forshee {
118825bded7cSSeth Forshee 	unsigned char param[4];
118925bded7cSSeth Forshee 
119024ba9707SKevin Cernekee 	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_RESET_WRAP, param)) {
119125bded7cSSeth Forshee 		psmouse_err(psmouse, "failed to enter command mode\n");
119225bded7cSSeth Forshee 		return -1;
119325bded7cSSeth Forshee 	}
119425bded7cSSeth Forshee 
119575af9e56SDave Turvene 	if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
119675af9e56SDave Turvene 	    param[0] != 0x73) {
119725bded7cSSeth Forshee 		psmouse_dbg(psmouse,
119824ba9707SKevin Cernekee 			    "unknown response while entering command mode\n");
119925bded7cSSeth Forshee 		return -1;
120025bded7cSSeth Forshee 	}
120125bded7cSSeth Forshee 	return 0;
120225bded7cSSeth Forshee }
120325bded7cSSeth Forshee 
120425bded7cSSeth Forshee static inline int alps_exit_command_mode(struct psmouse *psmouse)
120525bded7cSSeth Forshee {
120625bded7cSSeth Forshee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
120725bded7cSSeth Forshee 	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM))
120825bded7cSSeth Forshee 		return -1;
120925bded7cSSeth Forshee 	return 0;
121025bded7cSSeth Forshee }
121125bded7cSSeth Forshee 
12121da177e4SLinus Torvalds /*
12131da177e4SLinus Torvalds  * For DualPoint devices select the device that should respond to
12141da177e4SLinus Torvalds  * subsequent commands. It looks like glidepad is behind stickpointer,
12151da177e4SLinus Torvalds  * I'd thought it would be other way around...
12161da177e4SLinus Torvalds  */
121725bded7cSSeth Forshee static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable)
12181da177e4SLinus Torvalds {
12191da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
12201da177e4SLinus Torvalds 	int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
12211da177e4SLinus Torvalds 
12221da177e4SLinus Torvalds 	if (ps2_command(ps2dev, NULL, cmd) ||
12231da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, cmd) ||
12241da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, cmd) ||
12251da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
12261da177e4SLinus Torvalds 		return -1;
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds 	/* we may get 3 more bytes, just ignore them */
1229c611763dSDmitry Torokhov 	ps2_drain(ps2dev, 3, 100);
12301da177e4SLinus Torvalds 
12311da177e4SLinus Torvalds 	return 0;
12321da177e4SLinus Torvalds }
12331da177e4SLinus Torvalds 
123425bded7cSSeth Forshee static int alps_absolute_mode_v1_v2(struct psmouse *psmouse)
12351da177e4SLinus Torvalds {
12361da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 	/* Try ALPS magic knock - 4 disable before enable */
12391da177e4SLinus Torvalds 	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
12401da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
12411da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
12421da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
12431da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
12441da177e4SLinus Torvalds 		return -1;
12451da177e4SLinus Torvalds 
12461da177e4SLinus Torvalds 	/*
12471da177e4SLinus Torvalds 	 * Switch mouse to poll (remote) mode so motion data will not
12481da177e4SLinus Torvalds 	 * get in our way
12491da177e4SLinus Torvalds 	 */
12501da177e4SLinus Torvalds 	return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
12511da177e4SLinus Torvalds }
12521da177e4SLinus Torvalds 
125395f75e91SYunkang Tang static int alps_monitor_mode_send_word(struct psmouse *psmouse, u16 word)
125495f75e91SYunkang Tang {
125595f75e91SYunkang Tang 	int i, nibble;
125695f75e91SYunkang Tang 
125795f75e91SYunkang Tang 	/*
125895f75e91SYunkang Tang 	 * b0-b11 are valid bits, send sequence is inverse.
125995f75e91SYunkang Tang 	 * e.g. when word = 0x0123, nibble send sequence is 3, 2, 1
126095f75e91SYunkang Tang 	 */
126195f75e91SYunkang Tang 	for (i = 0; i <= 8; i += 4) {
126295f75e91SYunkang Tang 		nibble = (word >> i) & 0xf;
126395f75e91SYunkang Tang 		if (alps_command_mode_send_nibble(psmouse, nibble))
126495f75e91SYunkang Tang 			return -1;
126595f75e91SYunkang Tang 	}
126695f75e91SYunkang Tang 
126795f75e91SYunkang Tang 	return 0;
126895f75e91SYunkang Tang }
126995f75e91SYunkang Tang 
127095f75e91SYunkang Tang static int alps_monitor_mode_write_reg(struct psmouse *psmouse,
127195f75e91SYunkang Tang 				       u16 addr, u16 value)
127295f75e91SYunkang Tang {
127395f75e91SYunkang Tang 	struct ps2dev *ps2dev = &psmouse->ps2dev;
127495f75e91SYunkang Tang 
127595f75e91SYunkang Tang 	/* 0x0A0 is the command to write the word */
127695f75e91SYunkang Tang 	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE) ||
127795f75e91SYunkang Tang 	    alps_monitor_mode_send_word(psmouse, 0x0A0) ||
127895f75e91SYunkang Tang 	    alps_monitor_mode_send_word(psmouse, addr) ||
127995f75e91SYunkang Tang 	    alps_monitor_mode_send_word(psmouse, value) ||
128095f75e91SYunkang Tang 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
128195f75e91SYunkang Tang 		return -1;
128295f75e91SYunkang Tang 
128395f75e91SYunkang Tang 	return 0;
128495f75e91SYunkang Tang }
128595f75e91SYunkang Tang 
128695f75e91SYunkang Tang static int alps_monitor_mode(struct psmouse *psmouse, bool enable)
128795f75e91SYunkang Tang {
128895f75e91SYunkang Tang 	struct ps2dev *ps2dev = &psmouse->ps2dev;
128995f75e91SYunkang Tang 
129095f75e91SYunkang Tang 	if (enable) {
129195f75e91SYunkang Tang 		/* EC E9 F5 F5 E7 E6 E7 E9 to enter monitor mode */
129295f75e91SYunkang Tang 		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
129395f75e91SYunkang Tang 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO) ||
129495f75e91SYunkang Tang 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
129595f75e91SYunkang Tang 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
129695f75e91SYunkang Tang 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
129795f75e91SYunkang Tang 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
129895f75e91SYunkang Tang 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
129995f75e91SYunkang Tang 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO))
130095f75e91SYunkang Tang 			return -1;
130195f75e91SYunkang Tang 	} else {
130295f75e91SYunkang Tang 		/* EC to exit monitor mode */
130395f75e91SYunkang Tang 		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP))
130495f75e91SYunkang Tang 			return -1;
130595f75e91SYunkang Tang 	}
130695f75e91SYunkang Tang 
130795f75e91SYunkang Tang 	return 0;
130895f75e91SYunkang Tang }
130995f75e91SYunkang Tang 
131095f75e91SYunkang Tang static int alps_absolute_mode_v6(struct psmouse *psmouse)
131195f75e91SYunkang Tang {
131295f75e91SYunkang Tang 	u16 reg_val = 0x181;
131395f75e91SYunkang Tang 	int ret = -1;
131495f75e91SYunkang Tang 
131595f75e91SYunkang Tang 	/* enter monitor mode, to write the register */
131695f75e91SYunkang Tang 	if (alps_monitor_mode(psmouse, true))
131795f75e91SYunkang Tang 		return -1;
131895f75e91SYunkang Tang 
131995f75e91SYunkang Tang 	ret = alps_monitor_mode_write_reg(psmouse, 0x000, reg_val);
132095f75e91SYunkang Tang 
132195f75e91SYunkang Tang 	if (alps_monitor_mode(psmouse, false))
132295f75e91SYunkang Tang 		ret = -1;
132395f75e91SYunkang Tang 
132495f75e91SYunkang Tang 	return ret;
132595f75e91SYunkang Tang }
132695f75e91SYunkang Tang 
13271da177e4SLinus Torvalds static int alps_get_status(struct psmouse *psmouse, char *param)
13281da177e4SLinus Torvalds {
13291da177e4SLinus Torvalds 	/* Get status: 0xF5 0xF5 0xF5 0xE9 */
133024ba9707SKevin Cernekee 	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_DISABLE, param))
13311da177e4SLinus Torvalds 		return -1;
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 	return 0;
13341da177e4SLinus Torvalds }
13351da177e4SLinus Torvalds 
13361da177e4SLinus Torvalds /*
13371da177e4SLinus Torvalds  * Turn touchpad tapping on or off. The sequences are:
13381da177e4SLinus Torvalds  * 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
13391da177e4SLinus Torvalds  * 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
13401da177e4SLinus Torvalds  * My guess that 0xE9 (GetInfo) is here as a sync point.
13411da177e4SLinus Torvalds  * For models that also have stickpointer (DualPoints) its tapping
13421da177e4SLinus Torvalds  * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
13431da177e4SLinus Torvalds  * we don't fiddle with it.
13441da177e4SLinus Torvalds  */
13451da177e4SLinus Torvalds static int alps_tap_mode(struct psmouse *psmouse, int enable)
13461da177e4SLinus Torvalds {
13471da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
13481da177e4SLinus Torvalds 	int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
13491da177e4SLinus Torvalds 	unsigned char tap_arg = enable ? 0x0A : 0x00;
13501da177e4SLinus Torvalds 	unsigned char param[4];
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
13531da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
13541da177e4SLinus Torvalds 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
13551da177e4SLinus Torvalds 	    ps2_command(ps2dev, &tap_arg, cmd))
13561da177e4SLinus Torvalds 		return -1;
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 	if (alps_get_status(psmouse, param))
13591da177e4SLinus Torvalds 		return -1;
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds 	return 0;
13621da177e4SLinus Torvalds }
13631da177e4SLinus Torvalds 
1364f0d5c6f4SDmitry Torokhov /*
1365f0d5c6f4SDmitry Torokhov  * alps_poll() - poll the touchpad for current motion packet.
1366f0d5c6f4SDmitry Torokhov  * Used in resync.
1367f0d5c6f4SDmitry Torokhov  */
1368f0d5c6f4SDmitry Torokhov static int alps_poll(struct psmouse *psmouse)
1369f0d5c6f4SDmitry Torokhov {
1370f0d5c6f4SDmitry Torokhov 	struct alps_data *priv = psmouse->private;
1371b46615feSSeth Forshee 	unsigned char buf[sizeof(psmouse->packet)];
1372b7802c5cSDmitry Torokhov 	bool poll_failed;
1373f0d5c6f4SDmitry Torokhov 
137499df65e7SKevin Cernekee 	if (priv->flags & ALPS_PASS)
137525bded7cSSeth Forshee 		alps_passthrough_mode_v2(psmouse, true);
1376f0d5c6f4SDmitry Torokhov 
1377f0d5c6f4SDmitry Torokhov 	poll_failed = ps2_command(&psmouse->ps2dev, buf,
1378f0d5c6f4SDmitry Torokhov 				  PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0;
1379f0d5c6f4SDmitry Torokhov 
138099df65e7SKevin Cernekee 	if (priv->flags & ALPS_PASS)
138125bded7cSSeth Forshee 		alps_passthrough_mode_v2(psmouse, false);
1382f0d5c6f4SDmitry Torokhov 
138399df65e7SKevin Cernekee 	if (poll_failed || (buf[0] & priv->mask0) != priv->byte0)
1384f0d5c6f4SDmitry Torokhov 		return -1;
1385f0d5c6f4SDmitry Torokhov 
1386f0d5c6f4SDmitry Torokhov 	if ((psmouse->badbyte & 0xc8) == 0x08) {
1387f0d5c6f4SDmitry Torokhov /*
1388f0d5c6f4SDmitry Torokhov  * Poll the track stick ...
1389f0d5c6f4SDmitry Torokhov  */
1390f0d5c6f4SDmitry Torokhov 		if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8)))
1391f0d5c6f4SDmitry Torokhov 			return -1;
1392f0d5c6f4SDmitry Torokhov 	}
1393f0d5c6f4SDmitry Torokhov 
1394f0d5c6f4SDmitry Torokhov 	memcpy(psmouse->packet, buf, sizeof(buf));
1395f0d5c6f4SDmitry Torokhov 	return 0;
1396f0d5c6f4SDmitry Torokhov }
1397f0d5c6f4SDmitry Torokhov 
139825bded7cSSeth Forshee static int alps_hw_init_v1_v2(struct psmouse *psmouse)
13991da177e4SLinus Torvalds {
14001da177e4SLinus Torvalds 	struct alps_data *priv = psmouse->private;
14011da177e4SLinus Torvalds 
140299df65e7SKevin Cernekee 	if ((priv->flags & ALPS_PASS) &&
140325bded7cSSeth Forshee 	    alps_passthrough_mode_v2(psmouse, true)) {
14041da177e4SLinus Torvalds 		return -1;
1405b7802c5cSDmitry Torokhov 	}
14061da177e4SLinus Torvalds 
1407b7802c5cSDmitry Torokhov 	if (alps_tap_mode(psmouse, true)) {
1408b5d21704SDmitry Torokhov 		psmouse_warn(psmouse, "Failed to enable hardware tapping\n");
14091da177e4SLinus Torvalds 		return -1;
1410963f626dSPeter Osterlund 	}
14111da177e4SLinus Torvalds 
141225bded7cSSeth Forshee 	if (alps_absolute_mode_v1_v2(psmouse)) {
1413b5d21704SDmitry Torokhov 		psmouse_err(psmouse, "Failed to enable absolute mode\n");
14141da177e4SLinus Torvalds 		return -1;
14151da177e4SLinus Torvalds 	}
14161da177e4SLinus Torvalds 
141799df65e7SKevin Cernekee 	if ((priv->flags & ALPS_PASS) &&
141825bded7cSSeth Forshee 	    alps_passthrough_mode_v2(psmouse, false)) {
14191da177e4SLinus Torvalds 		return -1;
1420b7802c5cSDmitry Torokhov 	}
14211da177e4SLinus Torvalds 
14221e0c5b12SDmitry Torokhov 	/* ALPS needs stream mode, otherwise it won't report any data */
14231e0c5b12SDmitry Torokhov 	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) {
1424b5d21704SDmitry Torokhov 		psmouse_err(psmouse, "Failed to enable stream mode\n");
14251e0c5b12SDmitry Torokhov 		return -1;
14261e0c5b12SDmitry Torokhov 	}
14271e0c5b12SDmitry Torokhov 
14281e0c5b12SDmitry Torokhov 	return 0;
14291e0c5b12SDmitry Torokhov }
14301e0c5b12SDmitry Torokhov 
143195f75e91SYunkang Tang static int alps_hw_init_v6(struct psmouse *psmouse)
143295f75e91SYunkang Tang {
143395f75e91SYunkang Tang 	unsigned char param[2] = {0xC8, 0x14};
143495f75e91SYunkang Tang 
143595f75e91SYunkang Tang 	/* Enter passthrough mode to let trackpoint enter 6byte raw mode */
143695f75e91SYunkang Tang 	if (alps_passthrough_mode_v2(psmouse, true))
143795f75e91SYunkang Tang 		return -1;
143895f75e91SYunkang Tang 
143995f75e91SYunkang Tang 	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
144095f75e91SYunkang Tang 	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
144195f75e91SYunkang Tang 	    ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
144295f75e91SYunkang Tang 	    ps2_command(&psmouse->ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
144395f75e91SYunkang Tang 	    ps2_command(&psmouse->ps2dev, &param[1], PSMOUSE_CMD_SETRATE))
144495f75e91SYunkang Tang 		return -1;
144595f75e91SYunkang Tang 
144695f75e91SYunkang Tang 	if (alps_passthrough_mode_v2(psmouse, false))
144795f75e91SYunkang Tang 		return -1;
144895f75e91SYunkang Tang 
144995f75e91SYunkang Tang 	if (alps_absolute_mode_v6(psmouse)) {
145095f75e91SYunkang Tang 		psmouse_err(psmouse, "Failed to enable absolute mode\n");
145195f75e91SYunkang Tang 		return -1;
145295f75e91SYunkang Tang 	}
145395f75e91SYunkang Tang 
145495f75e91SYunkang Tang 	return 0;
145595f75e91SYunkang Tang }
145695f75e91SYunkang Tang 
145725bded7cSSeth Forshee /*
1458cd401204SKevin Cernekee  * Enable or disable passthrough mode to the trackstick.
145925bded7cSSeth Forshee  */
1460cd401204SKevin Cernekee static int alps_passthrough_mode_v3(struct psmouse *psmouse,
1461cd401204SKevin Cernekee 				    int reg_base, bool enable)
146225bded7cSSeth Forshee {
1463cd401204SKevin Cernekee 	int reg_val, ret = -1;
146425bded7cSSeth Forshee 
1465d18e53fcSKevin Cernekee 	if (alps_enter_command_mode(psmouse))
146625bded7cSSeth Forshee 		return -1;
146725bded7cSSeth Forshee 
1468cd401204SKevin Cernekee 	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008);
1469cd401204SKevin Cernekee 	if (reg_val == -1)
1470cd401204SKevin Cernekee 		goto error;
1471cd401204SKevin Cernekee 
147225bded7cSSeth Forshee 	if (enable)
147325bded7cSSeth Forshee 		reg_val |= 0x01;
147425bded7cSSeth Forshee 	else
147525bded7cSSeth Forshee 		reg_val &= ~0x01;
147625bded7cSSeth Forshee 
1477cd401204SKevin Cernekee 	ret = __alps_command_mode_write_reg(psmouse, reg_val);
147825bded7cSSeth Forshee 
1479cd401204SKevin Cernekee error:
1480cd401204SKevin Cernekee 	if (alps_exit_command_mode(psmouse))
1481cd401204SKevin Cernekee 		ret = -1;
1482cd401204SKevin Cernekee 	return ret;
148325bded7cSSeth Forshee }
148425bded7cSSeth Forshee 
148525bded7cSSeth Forshee /* Must be in command mode when calling this function */
148625bded7cSSeth Forshee static int alps_absolute_mode_v3(struct psmouse *psmouse)
148725bded7cSSeth Forshee {
148825bded7cSSeth Forshee 	int reg_val;
148925bded7cSSeth Forshee 
149025bded7cSSeth Forshee 	reg_val = alps_command_mode_read_reg(psmouse, 0x0004);
149125bded7cSSeth Forshee 	if (reg_val == -1)
149225bded7cSSeth Forshee 		return -1;
149325bded7cSSeth Forshee 
149425bded7cSSeth Forshee 	reg_val |= 0x06;
149525bded7cSSeth Forshee 	if (__alps_command_mode_write_reg(psmouse, reg_val))
149625bded7cSSeth Forshee 		return -1;
149725bded7cSSeth Forshee 
149825bded7cSSeth Forshee 	return 0;
149925bded7cSSeth Forshee }
150025bded7cSSeth Forshee 
1501cd401204SKevin Cernekee static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
150225bded7cSSeth Forshee {
1503cd401204SKevin Cernekee 	int ret = -EIO, reg_val;
150425bded7cSSeth Forshee 
1505d18e53fcSKevin Cernekee 	if (alps_enter_command_mode(psmouse))
150625bded7cSSeth Forshee 		goto error;
150725bded7cSSeth Forshee 
1508cd401204SKevin Cernekee 	reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08);
150925bded7cSSeth Forshee 	if (reg_val == -1)
151025bded7cSSeth Forshee 		goto error;
1511cd401204SKevin Cernekee 
1512cd401204SKevin Cernekee 	/* bit 7: trackstick is present */
1513cd401204SKevin Cernekee 	ret = reg_val & 0x80 ? 0 : -ENODEV;
1514cd401204SKevin Cernekee 
1515cd401204SKevin Cernekee error:
1516cd401204SKevin Cernekee 	alps_exit_command_mode(psmouse);
1517cd401204SKevin Cernekee 	return ret;
1518cd401204SKevin Cernekee }
1519cd401204SKevin Cernekee 
1520cd401204SKevin Cernekee static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
1521cd401204SKevin Cernekee {
1522cd401204SKevin Cernekee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
1523cd401204SKevin Cernekee 	int ret = 0;
1524cd401204SKevin Cernekee 	unsigned char param[4];
1525cd401204SKevin Cernekee 
1526cd401204SKevin Cernekee 	if (alps_passthrough_mode_v3(psmouse, reg_base, true))
1527cd401204SKevin Cernekee 		return -EIO;
152825bded7cSSeth Forshee 
152925bded7cSSeth Forshee 	/*
153025bded7cSSeth Forshee 	 * E7 report for the trackstick
153125bded7cSSeth Forshee 	 *
153225bded7cSSeth Forshee 	 * There have been reports of failures to seem to trace back
153325bded7cSSeth Forshee 	 * to the above trackstick check failing. When these occur
153425bded7cSSeth Forshee 	 * this E7 report fails, so when that happens we continue
153525bded7cSSeth Forshee 	 * with the assumption that there isn't a trackstick after
153625bded7cSSeth Forshee 	 * all.
153725bded7cSSeth Forshee 	 */
1538cd401204SKevin Cernekee 	if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) {
153925bded7cSSeth Forshee 		psmouse_warn(psmouse, "trackstick E7 report failed\n");
1540cd401204SKevin Cernekee 		ret = -ENODEV;
154125bded7cSSeth Forshee 	} else {
154239fbe585SDmitry Torokhov 		psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
154325bded7cSSeth Forshee 
154425bded7cSSeth Forshee 		/*
154525bded7cSSeth Forshee 		 * Not sure what this does, but it is absolutely
154625bded7cSSeth Forshee 		 * essential. Without it, the touchpad does not
154725bded7cSSeth Forshee 		 * work at all and the trackstick just emits normal
154825bded7cSSeth Forshee 		 * PS/2 packets.
154925bded7cSSeth Forshee 		 */
155025bded7cSSeth Forshee 		if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
155125bded7cSSeth Forshee 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
155225bded7cSSeth Forshee 		    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
155325bded7cSSeth Forshee 		    alps_command_mode_send_nibble(psmouse, 0x9) ||
155425bded7cSSeth Forshee 		    alps_command_mode_send_nibble(psmouse, 0x4)) {
155525bded7cSSeth Forshee 			psmouse_err(psmouse,
155625bded7cSSeth Forshee 				    "Error sending magic E6 sequence\n");
1557cd401204SKevin Cernekee 			ret = -EIO;
155825bded7cSSeth Forshee 			goto error;
155925bded7cSSeth Forshee 		}
156025bded7cSSeth Forshee 
1561cd401204SKevin Cernekee 		/*
1562cd401204SKevin Cernekee 		 * This ensures the trackstick packets are in the format
1563cd401204SKevin Cernekee 		 * supported by this driver. If bit 1 isn't set the packet
1564cd401204SKevin Cernekee 		 * format is different.
1565cd401204SKevin Cernekee 		 */
1566d18e53fcSKevin Cernekee 		if (alps_enter_command_mode(psmouse) ||
1567cd401204SKevin Cernekee 		    alps_command_mode_write_reg(psmouse,
1568cd401204SKevin Cernekee 						reg_base + 0x08, 0x82) ||
1569cd401204SKevin Cernekee 		    alps_exit_command_mode(psmouse))
1570cd401204SKevin Cernekee 			ret = -EIO;
1571cd401204SKevin Cernekee 	}
1572cd401204SKevin Cernekee 
1573cd401204SKevin Cernekee error:
1574cd401204SKevin Cernekee 	if (alps_passthrough_mode_v3(psmouse, reg_base, false))
1575cd401204SKevin Cernekee 		ret = -EIO;
1576cd401204SKevin Cernekee 
1577cd401204SKevin Cernekee 	return ret;
1578cd401204SKevin Cernekee }
1579cd401204SKevin Cernekee 
1580cd401204SKevin Cernekee static int alps_hw_init_v3(struct psmouse *psmouse)
1581cd401204SKevin Cernekee {
1582cd401204SKevin Cernekee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
1583cd401204SKevin Cernekee 	int reg_val;
1584cd401204SKevin Cernekee 	unsigned char param[4];
1585cd401204SKevin Cernekee 
1586cd401204SKevin Cernekee 	reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
1587cd401204SKevin Cernekee 	if (reg_val == -EIO)
1588cd401204SKevin Cernekee 		goto error;
158939fbe585SDmitry Torokhov 
1590cd401204SKevin Cernekee 	if (reg_val == 0 &&
1591cd401204SKevin Cernekee 	    alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
1592cd401204SKevin Cernekee 		goto error;
1593cd401204SKevin Cernekee 
1594d18e53fcSKevin Cernekee 	if (alps_enter_command_mode(psmouse) ||
1595cd401204SKevin Cernekee 	    alps_absolute_mode_v3(psmouse)) {
159625bded7cSSeth Forshee 		psmouse_err(psmouse, "Failed to enter absolute mode\n");
159725bded7cSSeth Forshee 		goto error;
159825bded7cSSeth Forshee 	}
159925bded7cSSeth Forshee 
160025bded7cSSeth Forshee 	reg_val = alps_command_mode_read_reg(psmouse, 0x0006);
160125bded7cSSeth Forshee 	if (reg_val == -1)
160225bded7cSSeth Forshee 		goto error;
160325bded7cSSeth Forshee 	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01))
160425bded7cSSeth Forshee 		goto error;
160525bded7cSSeth Forshee 
160625bded7cSSeth Forshee 	reg_val = alps_command_mode_read_reg(psmouse, 0x0007);
160725bded7cSSeth Forshee 	if (reg_val == -1)
160825bded7cSSeth Forshee 		goto error;
160925bded7cSSeth Forshee 	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01))
161025bded7cSSeth Forshee 		goto error;
161125bded7cSSeth Forshee 
161225bded7cSSeth Forshee 	if (alps_command_mode_read_reg(psmouse, 0x0144) == -1)
161325bded7cSSeth Forshee 		goto error;
161425bded7cSSeth Forshee 	if (__alps_command_mode_write_reg(psmouse, 0x04))
161525bded7cSSeth Forshee 		goto error;
161625bded7cSSeth Forshee 
161725bded7cSSeth Forshee 	if (alps_command_mode_read_reg(psmouse, 0x0159) == -1)
161825bded7cSSeth Forshee 		goto error;
161925bded7cSSeth Forshee 	if (__alps_command_mode_write_reg(psmouse, 0x03))
162025bded7cSSeth Forshee 		goto error;
162125bded7cSSeth Forshee 
162225bded7cSSeth Forshee 	if (alps_command_mode_read_reg(psmouse, 0x0163) == -1)
162325bded7cSSeth Forshee 		goto error;
162425bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03))
162525bded7cSSeth Forshee 		goto error;
162625bded7cSSeth Forshee 
162725bded7cSSeth Forshee 	if (alps_command_mode_read_reg(psmouse, 0x0162) == -1)
162825bded7cSSeth Forshee 		goto error;
162925bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04))
163025bded7cSSeth Forshee 		goto error;
163125bded7cSSeth Forshee 
163225bded7cSSeth Forshee 	alps_exit_command_mode(psmouse);
163325bded7cSSeth Forshee 
163425bded7cSSeth Forshee 	/* Set rate and enable data reporting */
163525bded7cSSeth Forshee 	param[0] = 0x64;
163625bded7cSSeth Forshee 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) ||
163725bded7cSSeth Forshee 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
163825bded7cSSeth Forshee 		psmouse_err(psmouse, "Failed to enable data reporting\n");
163925bded7cSSeth Forshee 		return -1;
164025bded7cSSeth Forshee 	}
164125bded7cSSeth Forshee 
164225bded7cSSeth Forshee 	return 0;
164325bded7cSSeth Forshee 
164425bded7cSSeth Forshee error:
164525bded7cSSeth Forshee 	/*
164625bded7cSSeth Forshee 	 * Leaving the touchpad in command mode will essentially render
164725bded7cSSeth Forshee 	 * it unusable until the machine reboots, so exit it here just
164825bded7cSSeth Forshee 	 * to be safe
164925bded7cSSeth Forshee 	 */
165025bded7cSSeth Forshee 	alps_exit_command_mode(psmouse);
165125bded7cSSeth Forshee 	return -1;
165225bded7cSSeth Forshee }
165325bded7cSSeth Forshee 
16541302bac3SKevin Cernekee static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
16551302bac3SKevin Cernekee {
1656cd401204SKevin Cernekee 	struct alps_data *priv = psmouse->private;
16571302bac3SKevin Cernekee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
16581302bac3SKevin Cernekee 	int reg_val, ret = -1;
16591302bac3SKevin Cernekee 
1660cd401204SKevin Cernekee 	if (priv->flags & ALPS_DUALPOINT) {
1661cd401204SKevin Cernekee 		reg_val = alps_setup_trackstick_v3(psmouse,
1662cd401204SKevin Cernekee 						   ALPS_REG_BASE_RUSHMORE);
1663cd401204SKevin Cernekee 		if (reg_val == -EIO)
1664cd401204SKevin Cernekee 			goto error;
1665cd401204SKevin Cernekee 		if (reg_val == -ENODEV)
1666cd401204SKevin Cernekee 			priv->flags &= ~ALPS_DUALPOINT;
1667cd401204SKevin Cernekee 	}
1668cd401204SKevin Cernekee 
1669d18e53fcSKevin Cernekee 	if (alps_enter_command_mode(psmouse) ||
16701302bac3SKevin Cernekee 	    alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 ||
16711302bac3SKevin Cernekee 	    alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
16721302bac3SKevin Cernekee 		goto error;
16731302bac3SKevin Cernekee 
16741302bac3SKevin Cernekee 	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
16751302bac3SKevin Cernekee 	if (reg_val == -1)
16761302bac3SKevin Cernekee 		goto error;
16771302bac3SKevin Cernekee 	if (__alps_command_mode_write_reg(psmouse, reg_val & 0xfd))
16781302bac3SKevin Cernekee 		goto error;
16791302bac3SKevin Cernekee 
16801302bac3SKevin Cernekee 	if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
16811302bac3SKevin Cernekee 		goto error;
16821302bac3SKevin Cernekee 
16831302bac3SKevin Cernekee 	/* enter absolute mode */
16841302bac3SKevin Cernekee 	reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
16851302bac3SKevin Cernekee 	if (reg_val == -1)
16861302bac3SKevin Cernekee 		goto error;
16871302bac3SKevin Cernekee 	if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
16881302bac3SKevin Cernekee 		goto error;
16891302bac3SKevin Cernekee 
16901302bac3SKevin Cernekee 	alps_exit_command_mode(psmouse);
16911302bac3SKevin Cernekee 	return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
16921302bac3SKevin Cernekee 
16931302bac3SKevin Cernekee error:
16941302bac3SKevin Cernekee 	alps_exit_command_mode(psmouse);
16951302bac3SKevin Cernekee 	return ret;
16961302bac3SKevin Cernekee }
16971302bac3SKevin Cernekee 
169825bded7cSSeth Forshee /* Must be in command mode when calling this function */
169925bded7cSSeth Forshee static int alps_absolute_mode_v4(struct psmouse *psmouse)
170025bded7cSSeth Forshee {
170125bded7cSSeth Forshee 	int reg_val;
170225bded7cSSeth Forshee 
170325bded7cSSeth Forshee 	reg_val = alps_command_mode_read_reg(psmouse, 0x0004);
170425bded7cSSeth Forshee 	if (reg_val == -1)
170525bded7cSSeth Forshee 		return -1;
170625bded7cSSeth Forshee 
170725bded7cSSeth Forshee 	reg_val |= 0x02;
170825bded7cSSeth Forshee 	if (__alps_command_mode_write_reg(psmouse, reg_val))
170925bded7cSSeth Forshee 		return -1;
171025bded7cSSeth Forshee 
171125bded7cSSeth Forshee 	return 0;
171225bded7cSSeth Forshee }
171325bded7cSSeth Forshee 
171425bded7cSSeth Forshee static int alps_hw_init_v4(struct psmouse *psmouse)
171525bded7cSSeth Forshee {
171625bded7cSSeth Forshee 	struct ps2dev *ps2dev = &psmouse->ps2dev;
171725bded7cSSeth Forshee 	unsigned char param[4];
171825bded7cSSeth Forshee 
1719d18e53fcSKevin Cernekee 	if (alps_enter_command_mode(psmouse))
172025bded7cSSeth Forshee 		goto error;
172125bded7cSSeth Forshee 
172225bded7cSSeth Forshee 	if (alps_absolute_mode_v4(psmouse)) {
172325bded7cSSeth Forshee 		psmouse_err(psmouse, "Failed to enter absolute mode\n");
172425bded7cSSeth Forshee 		goto error;
172525bded7cSSeth Forshee 	}
172625bded7cSSeth Forshee 
172725bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c))
172825bded7cSSeth Forshee 		goto error;
172925bded7cSSeth Forshee 
173025bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03))
173125bded7cSSeth Forshee 		goto error;
173225bded7cSSeth Forshee 
173325bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03))
173425bded7cSSeth Forshee 		goto error;
173525bded7cSSeth Forshee 
173625bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15))
173725bded7cSSeth Forshee 		goto error;
173825bded7cSSeth Forshee 
173925bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01))
174025bded7cSSeth Forshee 		goto error;
174125bded7cSSeth Forshee 
174225bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03))
174325bded7cSSeth Forshee 		goto error;
174425bded7cSSeth Forshee 
174525bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03))
174625bded7cSSeth Forshee 		goto error;
174725bded7cSSeth Forshee 
174825bded7cSSeth Forshee 	if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03))
174925bded7cSSeth Forshee 		goto error;
175025bded7cSSeth Forshee 
175125bded7cSSeth Forshee 	alps_exit_command_mode(psmouse);
175225bded7cSSeth Forshee 
175325bded7cSSeth Forshee 	/*
175425bded7cSSeth Forshee 	 * This sequence changes the output from a 9-byte to an
175525bded7cSSeth Forshee 	 * 8-byte format. All the same data seems to be present,
175625bded7cSSeth Forshee 	 * just in a more compact format.
175725bded7cSSeth Forshee 	 */
175825bded7cSSeth Forshee 	param[0] = 0xc8;
175925bded7cSSeth Forshee 	param[1] = 0x64;
176025bded7cSSeth Forshee 	param[2] = 0x50;
176125bded7cSSeth Forshee 	if (ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
176225bded7cSSeth Forshee 	    ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE) ||
176325bded7cSSeth Forshee 	    ps2_command(ps2dev, &param[2], PSMOUSE_CMD_SETRATE) ||
176425bded7cSSeth Forshee 	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
176525bded7cSSeth Forshee 		return -1;
176625bded7cSSeth Forshee 
176725bded7cSSeth Forshee 	/* Set rate and enable data reporting */
176825bded7cSSeth Forshee 	param[0] = 0x64;
176925bded7cSSeth Forshee 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) ||
177025bded7cSSeth Forshee 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
177125bded7cSSeth Forshee 		psmouse_err(psmouse, "Failed to enable data reporting\n");
177225bded7cSSeth Forshee 		return -1;
177325bded7cSSeth Forshee 	}
177425bded7cSSeth Forshee 
177525bded7cSSeth Forshee 	return 0;
177625bded7cSSeth Forshee 
177725bded7cSSeth Forshee error:
177825bded7cSSeth Forshee 	/*
177925bded7cSSeth Forshee 	 * Leaving the touchpad in command mode will essentially render
178025bded7cSSeth Forshee 	 * it unusable until the machine reboots, so exit it here just
178125bded7cSSeth Forshee 	 * to be safe
178225bded7cSSeth Forshee 	 */
178325bded7cSSeth Forshee 	alps_exit_command_mode(psmouse);
178425bded7cSSeth Forshee 	return -1;
178525bded7cSSeth Forshee }
178625bded7cSSeth Forshee 
1787ee65d4b3SYunkang Tang static int alps_dolphin_get_device_area(struct psmouse *psmouse,
1788ee65d4b3SYunkang Tang 					struct alps_data *priv)
1789ee65d4b3SYunkang Tang {
1790ee65d4b3SYunkang Tang 	struct ps2dev *ps2dev = &psmouse->ps2dev;
1791ee65d4b3SYunkang Tang 	unsigned char param[4] = {0};
1792ee65d4b3SYunkang Tang 	int num_x_electrode, num_y_electrode;
1793ee65d4b3SYunkang Tang 
1794ee65d4b3SYunkang Tang 	if (alps_enter_command_mode(psmouse))
1795ee65d4b3SYunkang Tang 		return -1;
1796ee65d4b3SYunkang Tang 
1797ee65d4b3SYunkang Tang 	param[0] = 0x0a;
1798ee65d4b3SYunkang Tang 	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
1799ee65d4b3SYunkang Tang 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
1800ee65d4b3SYunkang Tang 	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) ||
1801ee65d4b3SYunkang Tang 	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
1802ee65d4b3SYunkang Tang 	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE))
1803ee65d4b3SYunkang Tang 		return -1;
1804ee65d4b3SYunkang Tang 
1805ee65d4b3SYunkang Tang 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
1806ee65d4b3SYunkang Tang 		return -1;
1807ee65d4b3SYunkang Tang 
1808ee65d4b3SYunkang Tang 	/*
1809ee65d4b3SYunkang Tang 	 * Dolphin's sensor line number is not fixed. It can be calculated
1810ee65d4b3SYunkang Tang 	 * by adding the device's register value with DOLPHIN_PROFILE_X/YOFFSET.
1811ee65d4b3SYunkang Tang 	 * Further more, we can get device's x_max and y_max by multiplying
1812ee65d4b3SYunkang Tang 	 * sensor line number with DOLPHIN_COUNT_PER_ELECTRODE.
1813ee65d4b3SYunkang Tang 	 *
1814ee65d4b3SYunkang Tang 	 * e.g. When we get register's sensor_x = 11 & sensor_y = 8,
1815ee65d4b3SYunkang Tang 	 *	real sensor line number X = 11 + 8 = 19, and
1816ee65d4b3SYunkang Tang 	 *	real sensor line number Y = 8 + 1 = 9.
1817ee65d4b3SYunkang Tang 	 *	So, x_max = (19 - 1) * 64 = 1152, and
1818ee65d4b3SYunkang Tang 	 *	    y_max = (9 - 1) * 64 = 512.
1819ee65d4b3SYunkang Tang 	 */
1820ee65d4b3SYunkang Tang 	num_x_electrode = DOLPHIN_PROFILE_XOFFSET + (param[2] & 0x0F);
1821ee65d4b3SYunkang Tang 	num_y_electrode = DOLPHIN_PROFILE_YOFFSET + ((param[2] >> 4) & 0x0F);
1822ee65d4b3SYunkang Tang 	priv->x_bits = num_x_electrode;
1823ee65d4b3SYunkang Tang 	priv->y_bits = num_y_electrode;
1824ee65d4b3SYunkang Tang 	priv->x_max = (num_x_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE;
1825ee65d4b3SYunkang Tang 	priv->y_max = (num_y_electrode - 1) * DOLPHIN_COUNT_PER_ELECTRODE;
1826ee65d4b3SYunkang Tang 
1827ee65d4b3SYunkang Tang 	if (alps_exit_command_mode(psmouse))
1828ee65d4b3SYunkang Tang 		return -1;
1829ee65d4b3SYunkang Tang 
1830ee65d4b3SYunkang Tang 	return 0;
1831ee65d4b3SYunkang Tang }
1832ee65d4b3SYunkang Tang 
183375af9e56SDave Turvene static int alps_hw_init_dolphin_v1(struct psmouse *psmouse)
183475af9e56SDave Turvene {
183575af9e56SDave Turvene 	struct ps2dev *ps2dev = &psmouse->ps2dev;
183675af9e56SDave Turvene 	unsigned char param[2];
183775af9e56SDave Turvene 
183875af9e56SDave Turvene 	/* This is dolphin "v1" as empirically defined by florin9doi */
183975af9e56SDave Turvene 	param[0] = 0x64;
184075af9e56SDave Turvene 	param[1] = 0x28;
184175af9e56SDave Turvene 
184275af9e56SDave Turvene 	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
184375af9e56SDave Turvene 	    ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
184475af9e56SDave Turvene 	    ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE))
184575af9e56SDave Turvene 		return -1;
184675af9e56SDave Turvene 
184775af9e56SDave Turvene 	return 0;
184875af9e56SDave Turvene }
184975af9e56SDave Turvene 
185024af5cb9SKevin Cernekee static void alps_set_defaults(struct alps_data *priv)
185125bded7cSSeth Forshee {
1852f673ceb1SKevin Cernekee 	priv->byte0 = 0x8f;
1853f673ceb1SKevin Cernekee 	priv->mask0 = 0x8f;
1854f673ceb1SKevin Cernekee 	priv->flags = ALPS_DUALPOINT;
1855f673ceb1SKevin Cernekee 
18567a9f73e7SKevin Cernekee 	priv->x_max = 2000;
18577a9f73e7SKevin Cernekee 	priv->y_max = 1400;
18587a9f73e7SKevin Cernekee 	priv->x_bits = 15;
18597a9f73e7SKevin Cernekee 	priv->y_bits = 11;
18607a9f73e7SKevin Cernekee 
186199df65e7SKevin Cernekee 	switch (priv->proto_version) {
186225bded7cSSeth Forshee 	case ALPS_PROTO_V1:
186325bded7cSSeth Forshee 	case ALPS_PROTO_V2:
186424af5cb9SKevin Cernekee 		priv->hw_init = alps_hw_init_v1_v2;
186524af5cb9SKevin Cernekee 		priv->process_packet = alps_process_packet_v1_v2;
186624af5cb9SKevin Cernekee 		priv->set_abs_params = alps_set_abs_params_st;
186795f75e91SYunkang Tang 		priv->x_max = 1023;
186895f75e91SYunkang Tang 		priv->y_max = 767;
186925bded7cSSeth Forshee 		break;
187025bded7cSSeth Forshee 	case ALPS_PROTO_V3:
187124af5cb9SKevin Cernekee 		priv->hw_init = alps_hw_init_v3;
187224af5cb9SKevin Cernekee 		priv->process_packet = alps_process_packet_v3;
187324af5cb9SKevin Cernekee 		priv->set_abs_params = alps_set_abs_params_mt;
1874f85e5001SKevin Cernekee 		priv->decode_fields = alps_decode_pinnacle;
187550e8b216SKevin Cernekee 		priv->nibble_commands = alps_v3_nibble_commands;
187650e8b216SKevin Cernekee 		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
187725bded7cSSeth Forshee 		break;
187825bded7cSSeth Forshee 	case ALPS_PROTO_V4:
187924af5cb9SKevin Cernekee 		priv->hw_init = alps_hw_init_v4;
188024af5cb9SKevin Cernekee 		priv->process_packet = alps_process_packet_v4;
188124af5cb9SKevin Cernekee 		priv->set_abs_params = alps_set_abs_params_mt;
188250e8b216SKevin Cernekee 		priv->nibble_commands = alps_v4_nibble_commands;
188350e8b216SKevin Cernekee 		priv->addr_command = PSMOUSE_CMD_DISABLE;
188425bded7cSSeth Forshee 		break;
188575af9e56SDave Turvene 	case ALPS_PROTO_V5:
188675af9e56SDave Turvene 		priv->hw_init = alps_hw_init_dolphin_v1;
1887ee65d4b3SYunkang Tang 		priv->process_packet = alps_process_touchpad_packet_v3_v5;
188875af9e56SDave Turvene 		priv->decode_fields = alps_decode_dolphin;
188975af9e56SDave Turvene 		priv->set_abs_params = alps_set_abs_params_mt;
189075af9e56SDave Turvene 		priv->nibble_commands = alps_v3_nibble_commands;
189175af9e56SDave Turvene 		priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
189275af9e56SDave Turvene 		priv->byte0 = 0xc8;
1893ee65d4b3SYunkang Tang 		priv->mask0 = 0xd8;
189475af9e56SDave Turvene 		priv->flags = 0;
189575af9e56SDave Turvene 		priv->x_max = 1360;
189675af9e56SDave Turvene 		priv->y_max = 660;
189775af9e56SDave Turvene 		priv->x_bits = 23;
189875af9e56SDave Turvene 		priv->y_bits = 12;
189975af9e56SDave Turvene 		break;
190095f75e91SYunkang Tang 	case ALPS_PROTO_V6:
190195f75e91SYunkang Tang 		priv->hw_init = alps_hw_init_v6;
190295f75e91SYunkang Tang 		priv->process_packet = alps_process_packet_v6;
190395f75e91SYunkang Tang 		priv->set_abs_params = alps_set_abs_params_st;
190495f75e91SYunkang Tang 		priv->nibble_commands = alps_v6_nibble_commands;
190595f75e91SYunkang Tang 		priv->x_max = 2047;
190695f75e91SYunkang Tang 		priv->y_max = 1535;
190795f75e91SYunkang Tang 		break;
190825bded7cSSeth Forshee 	}
190925bded7cSSeth Forshee }
191025bded7cSSeth Forshee 
1911b5d6b851SKevin Cernekee static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv,
1912b5d6b851SKevin Cernekee 			    unsigned char *e7, unsigned char *ec)
19132e992cc0SKevin Cernekee {
1914b5d6b851SKevin Cernekee 	const struct alps_model_info *model;
19152e992cc0SKevin Cernekee 	int i;
19162e992cc0SKevin Cernekee 
1917b5d6b851SKevin Cernekee 	for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
1918b5d6b851SKevin Cernekee 		model = &alps_model_data[i];
1919b5d6b851SKevin Cernekee 
1920b5d6b851SKevin Cernekee 		if (!memcmp(e7, model->signature, sizeof(model->signature)) &&
1921b5d6b851SKevin Cernekee 		    (!model->command_mode_resp ||
1922b5d6b851SKevin Cernekee 		     model->command_mode_resp == ec[2])) {
1923b5d6b851SKevin Cernekee 
1924b5d6b851SKevin Cernekee 			priv->proto_version = model->proto_version;
192524af5cb9SKevin Cernekee 			alps_set_defaults(priv);
192624af5cb9SKevin Cernekee 
1927b5d6b851SKevin Cernekee 			priv->flags = model->flags;
1928b5d6b851SKevin Cernekee 			priv->byte0 = model->byte0;
1929b5d6b851SKevin Cernekee 			priv->mask0 = model->mask0;
1930b5d6b851SKevin Cernekee 
1931b5d6b851SKevin Cernekee 			return 0;
1932b5d6b851SKevin Cernekee 		}
1933b5d6b851SKevin Cernekee 	}
1934b5d6b851SKevin Cernekee 
1935b5d6b851SKevin Cernekee 	return -EINVAL;
1936b5d6b851SKevin Cernekee }
1937b5d6b851SKevin Cernekee 
1938b5d6b851SKevin Cernekee static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
1939b5d6b851SKevin Cernekee {
1940b5d6b851SKevin Cernekee 	unsigned char e6[4], e7[4], ec[4];
1941b5d6b851SKevin Cernekee 
19422e992cc0SKevin Cernekee 	/*
19432e992cc0SKevin Cernekee 	 * First try "E6 report".
19442e992cc0SKevin Cernekee 	 * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed.
19452e992cc0SKevin Cernekee 	 * The bits 0-2 of the first byte will be 1s if some buttons are
19462e992cc0SKevin Cernekee 	 * pressed.
19472e992cc0SKevin Cernekee 	 */
1948b5d6b851SKevin Cernekee 	if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
1949b5d6b851SKevin Cernekee 			 PSMOUSE_CMD_SETSCALE11, e6))
1950b5d6b851SKevin Cernekee 		return -EIO;
19512e992cc0SKevin Cernekee 
1952b5d6b851SKevin Cernekee 	if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10 && e6[2] != 100))
1953b5d6b851SKevin Cernekee 		return -EINVAL;
19542e992cc0SKevin Cernekee 
19552e992cc0SKevin Cernekee 	/*
1956b5d6b851SKevin Cernekee 	 * Now get the "E7" and "EC" reports.  These will uniquely identify
1957b5d6b851SKevin Cernekee 	 * most ALPS touchpads.
19582e992cc0SKevin Cernekee 	 */
1959b5d6b851SKevin Cernekee 	if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
1960b5d6b851SKevin Cernekee 			 PSMOUSE_CMD_SETSCALE21, e7) ||
1961b5d6b851SKevin Cernekee 	    alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
1962b5d6b851SKevin Cernekee 			 PSMOUSE_CMD_RESET_WRAP, ec) ||
1963b5d6b851SKevin Cernekee 	    alps_exit_command_mode(psmouse))
1964b5d6b851SKevin Cernekee 		return -EIO;
19652e992cc0SKevin Cernekee 
1966f673ceb1SKevin Cernekee 	if (alps_match_table(psmouse, priv, e7, ec) == 0) {
1967b5d6b851SKevin Cernekee 		return 0;
196875af9e56SDave Turvene 	} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
1969ee65d4b3SYunkang Tang 		   ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
197075af9e56SDave Turvene 		priv->proto_version = ALPS_PROTO_V5;
197175af9e56SDave Turvene 		alps_set_defaults(priv);
1972ee65d4b3SYunkang Tang 		if (alps_dolphin_get_device_area(psmouse, priv))
1973ee65d4b3SYunkang Tang 			return -EIO;
1974ee65d4b3SYunkang Tang 		else
197575af9e56SDave Turvene 			return 0;
19761302bac3SKevin Cernekee 	} else if (ec[0] == 0x88 && ec[1] == 0x08) {
19771302bac3SKevin Cernekee 		priv->proto_version = ALPS_PROTO_V3;
19781302bac3SKevin Cernekee 		alps_set_defaults(priv);
19791302bac3SKevin Cernekee 
19801302bac3SKevin Cernekee 		priv->hw_init = alps_hw_init_rushmore_v3;
19811302bac3SKevin Cernekee 		priv->decode_fields = alps_decode_rushmore;
19821302bac3SKevin Cernekee 		priv->x_bits = 16;
19831302bac3SKevin Cernekee 		priv->y_bits = 12;
19841302bac3SKevin Cernekee 
1985cd401204SKevin Cernekee 		/* hack to make addr_command, nibble_command available */
1986cd401204SKevin Cernekee 		psmouse->private = priv;
1987cd401204SKevin Cernekee 
1988cd401204SKevin Cernekee 		if (alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_RUSHMORE))
1989cd401204SKevin Cernekee 			priv->flags &= ~ALPS_DUALPOINT;
1990cd401204SKevin Cernekee 
19911302bac3SKevin Cernekee 		return 0;
1992f673ceb1SKevin Cernekee 	} else if (ec[0] == 0x88 && ec[1] == 0x07 &&
1993f673ceb1SKevin Cernekee 		   ec[2] >= 0x90 && ec[2] <= 0x9d) {
1994f673ceb1SKevin Cernekee 		priv->proto_version = ALPS_PROTO_V3;
1995f673ceb1SKevin Cernekee 		alps_set_defaults(priv);
1996f673ceb1SKevin Cernekee 
1997f673ceb1SKevin Cernekee 		return 0;
1998f673ceb1SKevin Cernekee 	}
19992e992cc0SKevin Cernekee 
2000b5d6b851SKevin Cernekee 	psmouse_info(psmouse,
200139fbe585SDmitry Torokhov 		     "Unknown ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
20022e992cc0SKevin Cernekee 
2003b5d6b851SKevin Cernekee 	return -EINVAL;
20042e992cc0SKevin Cernekee }
20052e992cc0SKevin Cernekee 
20061e0c5b12SDmitry Torokhov static int alps_reconnect(struct psmouse *psmouse)
20071e0c5b12SDmitry Torokhov {
2008b5d6b851SKevin Cernekee 	struct alps_data *priv = psmouse->private;
200971bb21b6SMaxim Levitsky 
20101e0c5b12SDmitry Torokhov 	psmouse_reset(psmouse);
20111e0c5b12SDmitry Torokhov 
2012b5d6b851SKevin Cernekee 	if (alps_identify(psmouse, priv) < 0)
20131e0c5b12SDmitry Torokhov 		return -1;
20141e0c5b12SDmitry Torokhov 
201524af5cb9SKevin Cernekee 	return priv->hw_init(psmouse);
20161da177e4SLinus Torvalds }
20171da177e4SLinus Torvalds 
20181da177e4SLinus Torvalds static void alps_disconnect(struct psmouse *psmouse)
20191da177e4SLinus Torvalds {
20201da177e4SLinus Torvalds 	struct alps_data *priv = psmouse->private;
20212e5b636bSDmitry Torokhov 
20221da177e4SLinus Torvalds 	psmouse_reset(psmouse);
20231d9f2626SSebastian Kapfer 	del_timer_sync(&priv->timer);
20242e5b636bSDmitry Torokhov 	input_unregister_device(priv->dev2);
20251da177e4SLinus Torvalds 	kfree(priv);
20261da177e4SLinus Torvalds }
20271da177e4SLinus Torvalds 
202824af5cb9SKevin Cernekee static void alps_set_abs_params_st(struct alps_data *priv,
202924af5cb9SKevin Cernekee 				   struct input_dev *dev1)
203024af5cb9SKevin Cernekee {
203195f75e91SYunkang Tang 	input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
203295f75e91SYunkang Tang 	input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
203324af5cb9SKevin Cernekee }
203424af5cb9SKevin Cernekee 
203524af5cb9SKevin Cernekee static void alps_set_abs_params_mt(struct alps_data *priv,
203624af5cb9SKevin Cernekee 				   struct input_dev *dev1)
203724af5cb9SKevin Cernekee {
203824af5cb9SKevin Cernekee 	set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
203924af5cb9SKevin Cernekee 	input_mt_init_slots(dev1, 2, 0);
20407a9f73e7SKevin Cernekee 	input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
20417a9f73e7SKevin Cernekee 	input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
204224af5cb9SKevin Cernekee 
204324af5cb9SKevin Cernekee 	set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
204424af5cb9SKevin Cernekee 	set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
204524af5cb9SKevin Cernekee 	set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
204624af5cb9SKevin Cernekee 
20477a9f73e7SKevin Cernekee 	input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
20487a9f73e7SKevin Cernekee 	input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
204924af5cb9SKevin Cernekee }
205024af5cb9SKevin Cernekee 
20511da177e4SLinus Torvalds int alps_init(struct psmouse *psmouse)
20521da177e4SLinus Torvalds {
20531da177e4SLinus Torvalds 	struct alps_data *priv;
20542e5b636bSDmitry Torokhov 	struct input_dev *dev1 = psmouse->dev, *dev2;
20551da177e4SLinus Torvalds 
2056f42649e8SDmitry Torokhov 	priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
20572e5b636bSDmitry Torokhov 	dev2 = input_allocate_device();
20582e5b636bSDmitry Torokhov 	if (!priv || !dev2)
20591da177e4SLinus Torvalds 		goto init_fail;
20602e5b636bSDmitry Torokhov 
20612e5b636bSDmitry Torokhov 	priv->dev2 = dev2;
20621d9f2626SSebastian Kapfer 	setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse);
20631d9f2626SSebastian Kapfer 
20641e0c5b12SDmitry Torokhov 	psmouse->private = priv;
20651da177e4SLinus Torvalds 
206625bded7cSSeth Forshee 	psmouse_reset(psmouse);
206725bded7cSSeth Forshee 
2068b5d6b851SKevin Cernekee 	if (alps_identify(psmouse, priv) < 0)
206971bb21b6SMaxim Levitsky 		goto init_fail;
207071bb21b6SMaxim Levitsky 
207124af5cb9SKevin Cernekee 	if (priv->hw_init(psmouse))
20721da177e4SLinus Torvalds 		goto init_fail;
20731da177e4SLinus Torvalds 
20747105d2eaSDmitry Torokhov 	/*
20757105d2eaSDmitry Torokhov 	 * Undo part of setup done for us by psmouse core since touchpad
20767105d2eaSDmitry Torokhov 	 * is not a relative device.
20777105d2eaSDmitry Torokhov 	 */
20787105d2eaSDmitry Torokhov 	__clear_bit(EV_REL, dev1->evbit);
20797105d2eaSDmitry Torokhov 	__clear_bit(REL_X, dev1->relbit);
20807105d2eaSDmitry Torokhov 	__clear_bit(REL_Y, dev1->relbit);
20817105d2eaSDmitry Torokhov 
20827105d2eaSDmitry Torokhov 	/*
20837105d2eaSDmitry Torokhov 	 * Now set up our capabilities.
20847105d2eaSDmitry Torokhov 	 */
20857b19ada2SJiri Slaby 	dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);
20867b19ada2SJiri Slaby 	dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
20877b19ada2SJiri Slaby 	dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER);
208871bb21b6SMaxim Levitsky 	dev1->keybit[BIT_WORD(BTN_LEFT)] |=
208971bb21b6SMaxim Levitsky 		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
20901da177e4SLinus Torvalds 
20917b19ada2SJiri Slaby 	dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
209225bded7cSSeth Forshee 
209324af5cb9SKevin Cernekee 	priv->set_abs_params(priv, dev1);
20942e5b636bSDmitry Torokhov 	input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
20951da177e4SLinus Torvalds 
209699df65e7SKevin Cernekee 	if (priv->flags & ALPS_WHEEL) {
20977b19ada2SJiri Slaby 		dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
20987b19ada2SJiri Slaby 		dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);
20991da177e4SLinus Torvalds 	}
21001da177e4SLinus Torvalds 
210199df65e7SKevin Cernekee 	if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
21027b19ada2SJiri Slaby 		dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);
21037b19ada2SJiri Slaby 		dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);
21041da177e4SLinus Torvalds 	}
21051da177e4SLinus Torvalds 
210699df65e7SKevin Cernekee 	if (priv->flags & ALPS_FOUR_BUTTONS) {
210771bb21b6SMaxim Levitsky 		dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);
210871bb21b6SMaxim Levitsky 		dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
210971bb21b6SMaxim Levitsky 		dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
211071bb21b6SMaxim Levitsky 		dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
211171bb21b6SMaxim Levitsky 	} else {
211271bb21b6SMaxim Levitsky 		dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
211371bb21b6SMaxim Levitsky 	}
211471bb21b6SMaxim Levitsky 
211508ffce45SDmitry Torokhov 	snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
21162e5b636bSDmitry Torokhov 	dev2->phys = priv->phys;
211799df65e7SKevin Cernekee 	dev2->name = (priv->flags & ALPS_DUALPOINT) ?
21189354f263SYunkang Tang 		     "DualPoint Stick" : "ALPS PS/2 Device";
21192e5b636bSDmitry Torokhov 	dev2->id.bustype = BUS_I8042;
21202e5b636bSDmitry Torokhov 	dev2->id.vendor  = 0x0002;
21212e5b636bSDmitry Torokhov 	dev2->id.product = PSMOUSE_ALPS;
21222e5b636bSDmitry Torokhov 	dev2->id.version = 0x0000;
21231db3a345SDmitry Torokhov 	dev2->dev.parent = &psmouse->ps2dev.serio->dev;
21241da177e4SLinus Torvalds 
21257b19ada2SJiri Slaby 	dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
212671bb21b6SMaxim Levitsky 	dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
212771bb21b6SMaxim Levitsky 	dev2->keybit[BIT_WORD(BTN_LEFT)] =
212871bb21b6SMaxim Levitsky 		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
21291da177e4SLinus Torvalds 
2130f42649e8SDmitry Torokhov 	if (input_register_device(priv->dev2))
2131f42649e8SDmitry Torokhov 		goto init_fail;
21321da177e4SLinus Torvalds 
21331da177e4SLinus Torvalds 	psmouse->protocol_handler = alps_process_byte;
2134f0d5c6f4SDmitry Torokhov 	psmouse->poll = alps_poll;
21351da177e4SLinus Torvalds 	psmouse->disconnect = alps_disconnect;
21361da177e4SLinus Torvalds 	psmouse->reconnect = alps_reconnect;
213799df65e7SKevin Cernekee 	psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6;
21381da177e4SLinus Torvalds 
2139f0d5c6f4SDmitry Torokhov 	/* We are having trouble resyncing ALPS touchpads so disable it for now */
2140f0d5c6f4SDmitry Torokhov 	psmouse->resync_time = 0;
2141f0d5c6f4SDmitry Torokhov 
21421da177e4SLinus Torvalds 	return 0;
21431da177e4SLinus Torvalds 
21441da177e4SLinus Torvalds init_fail:
2145f42649e8SDmitry Torokhov 	psmouse_reset(psmouse);
21462e5b636bSDmitry Torokhov 	input_free_device(dev2);
21471da177e4SLinus Torvalds 	kfree(priv);
21481e0c5b12SDmitry Torokhov 	psmouse->private = NULL;
21491da177e4SLinus Torvalds 	return -1;
21501da177e4SLinus Torvalds }
21511da177e4SLinus Torvalds 
2152b7802c5cSDmitry Torokhov int alps_detect(struct psmouse *psmouse, bool set_properties)
21531da177e4SLinus Torvalds {
2154b5d6b851SKevin Cernekee 	struct alps_data dummy;
21551da177e4SLinus Torvalds 
2156b5d6b851SKevin Cernekee 	if (alps_identify(psmouse, &dummy) < 0)
21571da177e4SLinus Torvalds 		return -1;
21581da177e4SLinus Torvalds 
21591da177e4SLinus Torvalds 	if (set_properties) {
21601da177e4SLinus Torvalds 		psmouse->vendor = "ALPS";
2161b5d6b851SKevin Cernekee 		psmouse->name = dummy.flags & ALPS_DUALPOINT ?
2162968ac842SDmitry Torokhov 				"DualPoint TouchPad" : "GlidePoint";
2163b5d6b851SKevin Cernekee 		psmouse->model = dummy.proto_version << 8;
21641da177e4SLinus Torvalds 	}
21651da177e4SLinus Torvalds 	return 0;
21661da177e4SLinus Torvalds }
21671da177e4SLinus Torvalds 
2168