xref: /openbmc/linux/drivers/input/mouse/byd.c (revision 652c0441)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
298ee3771SChris Diamand /*
398ee3771SChris Diamand  * BYD TouchPad PS/2 mouse driver
498ee3771SChris Diamand  *
598ee3771SChris Diamand  * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
682aaa086SChris Diamand  * Copyright (C) 2015 Richard Pospesel
782aaa086SChris Diamand  * Copyright (C) 2015 Tai Chi Minh Ralph Eastwood
882aaa086SChris Diamand  * Copyright (C) 2015 Martin Wimpress
982aaa086SChris Diamand  * Copyright (C) 2015 Jay Kuri
1098ee3771SChris Diamand  */
1198ee3771SChris Diamand 
1298ee3771SChris Diamand #include <linux/delay.h>
1398ee3771SChris Diamand #include <linux/input.h>
1498ee3771SChris Diamand #include <linux/libps2.h>
1598ee3771SChris Diamand #include <linux/serio.h>
162d5f5611SRichard Pospesel #include <linux/slab.h>
1798ee3771SChris Diamand 
1898ee3771SChris Diamand #include "psmouse.h"
1998ee3771SChris Diamand #include "byd.h"
2098ee3771SChris Diamand 
212d5f5611SRichard Pospesel /* PS2 Bits */
2298ee3771SChris Diamand #define PS2_Y_OVERFLOW	BIT_MASK(7)
2398ee3771SChris Diamand #define PS2_X_OVERFLOW	BIT_MASK(6)
2498ee3771SChris Diamand #define PS2_Y_SIGN	BIT_MASK(5)
2598ee3771SChris Diamand #define PS2_X_SIGN	BIT_MASK(4)
2698ee3771SChris Diamand #define PS2_ALWAYS_1	BIT_MASK(3)
2798ee3771SChris Diamand #define PS2_MIDDLE	BIT_MASK(2)
2898ee3771SChris Diamand #define PS2_RIGHT	BIT_MASK(1)
2998ee3771SChris Diamand #define PS2_LEFT	BIT_MASK(0)
3098ee3771SChris Diamand 
3198ee3771SChris Diamand /*
322d5f5611SRichard Pospesel  * BYD pad constants
3398ee3771SChris Diamand  */
3498ee3771SChris Diamand 
352d5f5611SRichard Pospesel /*
362d5f5611SRichard Pospesel  * True device resolution is unknown, however experiments show the
372d5f5611SRichard Pospesel  * resolution is about 111 units/mm.
382d5f5611SRichard Pospesel  * Absolute coordinate packets are in the range 0-255 for both X and Y
392d5f5611SRichard Pospesel  * we pick ABS_X/ABS_Y dimensions which are multiples of 256 and in
402d5f5611SRichard Pospesel  * the right ballpark given the touchpad's physical dimensions and estimate
412d5f5611SRichard Pospesel  * resolution per spec sheet, device active area dimensions are
422d5f5611SRichard Pospesel  * 101.6 x 60.1 mm.
432d5f5611SRichard Pospesel  */
442d5f5611SRichard Pospesel #define BYD_PAD_WIDTH		11264
452d5f5611SRichard Pospesel #define BYD_PAD_HEIGHT		6656
462d5f5611SRichard Pospesel #define BYD_PAD_RESOLUTION	111
4798ee3771SChris Diamand 
482d5f5611SRichard Pospesel /*
492d5f5611SRichard Pospesel  * Given the above dimensions, relative packets velocity is in multiples of
502d5f5611SRichard Pospesel  * 1 unit / 11 milliseconds.  We use this dt to estimate distance traveled
512d5f5611SRichard Pospesel  */
522d5f5611SRichard Pospesel #define BYD_DT			11
532d5f5611SRichard Pospesel /* Time in jiffies used to timeout various touch events (64 ms) */
542d5f5611SRichard Pospesel #define BYD_TOUCH_TIMEOUT	msecs_to_jiffies(64)
552d5f5611SRichard Pospesel 
562d5f5611SRichard Pospesel /* BYD commands reverse engineered from windows driver */
572d5f5611SRichard Pospesel 
582d5f5611SRichard Pospesel /*
592d5f5611SRichard Pospesel  * Swipe gesture from off-pad to on-pad
602d5f5611SRichard Pospesel  *  0 : disable
612d5f5611SRichard Pospesel  *  1 : enable
622d5f5611SRichard Pospesel  */
632d5f5611SRichard Pospesel #define BYD_CMD_SET_OFFSCREEN_SWIPE		0x10cc
642d5f5611SRichard Pospesel /*
652d5f5611SRichard Pospesel  * Tap and drag delay time
662d5f5611SRichard Pospesel  *  0 : disable
672d5f5611SRichard Pospesel  *  1 - 8 : least to most delay
682d5f5611SRichard Pospesel  */
692d5f5611SRichard Pospesel #define BYD_CMD_SET_TAP_DRAG_DELAY_TIME		0x10cf
702d5f5611SRichard Pospesel /*
712d5f5611SRichard Pospesel  * Physical buttons function mapping
722d5f5611SRichard Pospesel  *  0 : enable
732d5f5611SRichard Pospesel  *  4 : normal
742d5f5611SRichard Pospesel  *  5 : left button custom command
752d5f5611SRichard Pospesel  *  6 : right button custom command
762d5f5611SRichard Pospesel  *  8 : disable
772d5f5611SRichard Pospesel  */
782d5f5611SRichard Pospesel #define BYD_CMD_SET_PHYSICAL_BUTTONS		0x10d0
792d5f5611SRichard Pospesel /*
802d5f5611SRichard Pospesel  * Absolute mode (1 byte X/Y resolution)
812d5f5611SRichard Pospesel  *  0 : disable
822d5f5611SRichard Pospesel  *  2 : enable
832d5f5611SRichard Pospesel  */
842d5f5611SRichard Pospesel #define BYD_CMD_SET_ABSOLUTE_MODE		0x10d1
852d5f5611SRichard Pospesel /*
862d5f5611SRichard Pospesel  * Two finger scrolling
872d5f5611SRichard Pospesel  *  1 : vertical
882d5f5611SRichard Pospesel  *  2 : horizontal
892d5f5611SRichard Pospesel  *  3 : vertical + horizontal
902d5f5611SRichard Pospesel  *  4 : disable
912d5f5611SRichard Pospesel  */
922d5f5611SRichard Pospesel #define BYD_CMD_SET_TWO_FINGER_SCROLL		0x10d2
932d5f5611SRichard Pospesel /*
942d5f5611SRichard Pospesel  * Handedness
952d5f5611SRichard Pospesel  *  1 : right handed
962d5f5611SRichard Pospesel  *  2 : left handed
972d5f5611SRichard Pospesel  */
982d5f5611SRichard Pospesel #define BYD_CMD_SET_HANDEDNESS			0x10d3
992d5f5611SRichard Pospesel /*
1002d5f5611SRichard Pospesel  * Tap to click
1012d5f5611SRichard Pospesel  *  1 : enable
1022d5f5611SRichard Pospesel  *  2 : disable
1032d5f5611SRichard Pospesel  */
1042d5f5611SRichard Pospesel #define BYD_CMD_SET_TAP				0x10d4
1052d5f5611SRichard Pospesel /*
1062d5f5611SRichard Pospesel  * Tap and drag
1072d5f5611SRichard Pospesel  *  1 : tap and hold to drag
1082d5f5611SRichard Pospesel  *  2 : tap and hold to drag + lock
1092d5f5611SRichard Pospesel  *  3 : disable
1102d5f5611SRichard Pospesel  */
1112d5f5611SRichard Pospesel #define BYD_CMD_SET_TAP_DRAG			0x10d5
1122d5f5611SRichard Pospesel /*
1132d5f5611SRichard Pospesel  * Touch sensitivity
1142d5f5611SRichard Pospesel  *  1 - 7 : least to most sensitive
1152d5f5611SRichard Pospesel  */
1162d5f5611SRichard Pospesel #define BYD_CMD_SET_TOUCH_SENSITIVITY		0x10d6
1172d5f5611SRichard Pospesel /*
1182d5f5611SRichard Pospesel  * One finger scrolling
1192d5f5611SRichard Pospesel  *  1 : vertical
1202d5f5611SRichard Pospesel  *  2 : horizontal
1212d5f5611SRichard Pospesel  *  3 : vertical + horizontal
1222d5f5611SRichard Pospesel  *  4 : disable
1232d5f5611SRichard Pospesel  */
1242d5f5611SRichard Pospesel #define BYD_CMD_SET_ONE_FINGER_SCROLL		0x10d7
1252d5f5611SRichard Pospesel /*
1262d5f5611SRichard Pospesel  * One finger scrolling function
1272d5f5611SRichard Pospesel  *  1 : free scrolling
1282d5f5611SRichard Pospesel  *  2 : edge motion
1292d5f5611SRichard Pospesel  *  3 : free scrolling + edge motion
1302d5f5611SRichard Pospesel  *  4 : disable
1312d5f5611SRichard Pospesel  */
1322d5f5611SRichard Pospesel #define BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC	0x10d8
1332d5f5611SRichard Pospesel /*
1342d5f5611SRichard Pospesel  * Sliding speed
1352d5f5611SRichard Pospesel  *  1 - 5 : slowest to fastest
1362d5f5611SRichard Pospesel  */
1372d5f5611SRichard Pospesel #define BYD_CMD_SET_SLIDING_SPEED		0x10da
1382d5f5611SRichard Pospesel /*
1392d5f5611SRichard Pospesel  * Edge motion
1402d5f5611SRichard Pospesel  *  1 : disable
1412d5f5611SRichard Pospesel  *  2 : enable when dragging
1422d5f5611SRichard Pospesel  *  3 : enable when dragging and pointing
1432d5f5611SRichard Pospesel  */
1442d5f5611SRichard Pospesel #define BYD_CMD_SET_EDGE_MOTION			0x10db
1452d5f5611SRichard Pospesel /*
1462d5f5611SRichard Pospesel  * Left edge region size
1472d5f5611SRichard Pospesel  *  0 - 7 : smallest to largest width
1482d5f5611SRichard Pospesel  */
1492d5f5611SRichard Pospesel #define BYD_CMD_SET_LEFT_EDGE_REGION		0x10dc
1502d5f5611SRichard Pospesel /*
1512d5f5611SRichard Pospesel  * Top edge region size
1522d5f5611SRichard Pospesel  *  0 - 9 : smallest to largest height
1532d5f5611SRichard Pospesel  */
1542d5f5611SRichard Pospesel #define BYD_CMD_SET_TOP_EDGE_REGION		0x10dd
1552d5f5611SRichard Pospesel /*
1562d5f5611SRichard Pospesel  * Disregard palm press as clicks
1572d5f5611SRichard Pospesel  *  1 - 6 : smallest to largest
1582d5f5611SRichard Pospesel  */
1592d5f5611SRichard Pospesel #define BYD_CMD_SET_PALM_CHECK			0x10de
1602d5f5611SRichard Pospesel /*
1612d5f5611SRichard Pospesel  * Right edge region size
1622d5f5611SRichard Pospesel  *  0 - 7 : smallest to largest width
1632d5f5611SRichard Pospesel  */
1642d5f5611SRichard Pospesel #define BYD_CMD_SET_RIGHT_EDGE_REGION		0x10df
1652d5f5611SRichard Pospesel /*
1662d5f5611SRichard Pospesel  * Bottom edge region size
1672d5f5611SRichard Pospesel  *  0 - 9 : smallest to largest height
1682d5f5611SRichard Pospesel  */
1692d5f5611SRichard Pospesel #define BYD_CMD_SET_BOTTOM_EDGE_REGION		0x10e1
1702d5f5611SRichard Pospesel /*
1712d5f5611SRichard Pospesel  * Multitouch gestures
1722d5f5611SRichard Pospesel  *  1 : enable
1732d5f5611SRichard Pospesel  *  2 : disable
1742d5f5611SRichard Pospesel  */
1752d5f5611SRichard Pospesel #define BYD_CMD_SET_MULTITOUCH			0x10e3
1762d5f5611SRichard Pospesel /*
1772d5f5611SRichard Pospesel  * Edge motion speed
1782d5f5611SRichard Pospesel  *  0 : control with finger pressure
1792d5f5611SRichard Pospesel  *  1 - 9 : slowest to fastest
1802d5f5611SRichard Pospesel  */
1812d5f5611SRichard Pospesel #define BYD_CMD_SET_EDGE_MOTION_SPEED		0x10e4
1822d5f5611SRichard Pospesel /*
1832d5f5611SRichard Pospesel  * Two finger scolling function
1842d5f5611SRichard Pospesel  *  0 : free scrolling
1852d5f5611SRichard Pospesel  *  1 : free scrolling (with momentum)
1862d5f5611SRichard Pospesel  *  2 : edge motion
1872d5f5611SRichard Pospesel  *  3 : free scrolling (with momentum) + edge motion
1882d5f5611SRichard Pospesel  *  4 : disable
1892d5f5611SRichard Pospesel  */
1902d5f5611SRichard Pospesel #define BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC	0x10e5
1912d5f5611SRichard Pospesel 
1922d5f5611SRichard Pospesel /*
1932d5f5611SRichard Pospesel  * The touchpad generates a mixture of absolute and relative packets, indicated
194*652c0441SXiang wangx  * by the last byte of each packet being set to one of the following:
1952d5f5611SRichard Pospesel  */
1962d5f5611SRichard Pospesel #define BYD_PACKET_ABSOLUTE			0xf8
1972d5f5611SRichard Pospesel #define BYD_PACKET_RELATIVE			0x00
1982d5f5611SRichard Pospesel /* Multitouch gesture packets */
1992d5f5611SRichard Pospesel #define BYD_PACKET_PINCH_IN			0xd8
2002d5f5611SRichard Pospesel #define BYD_PACKET_PINCH_OUT			0x28
2012d5f5611SRichard Pospesel #define BYD_PACKET_ROTATE_CLOCKWISE		0x29
2022d5f5611SRichard Pospesel #define BYD_PACKET_ROTATE_ANTICLOCKWISE		0xd7
2032d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_RIGHT	0x2a
2042d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_DOWN	0x2b
2052d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_UP		0xd5
2062d5f5611SRichard Pospesel #define BYD_PACKET_TWO_FINGER_SCROLL_LEFT	0xd6
2072d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_RIGHT	0x2c
2082d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_DOWN	0x2d
2092d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_UP	0xd3
2102d5f5611SRichard Pospesel #define BYD_PACKET_THREE_FINGER_SWIPE_LEFT	0xd4
2112d5f5611SRichard Pospesel #define BYD_PACKET_FOUR_FINGER_DOWN		0x33
2122d5f5611SRichard Pospesel #define BYD_PACKET_FOUR_FINGER_UP		0xcd
2132d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_RIGHT		0x35
2142d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_DOWN		0x36
2152d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_UP		0xca
2162d5f5611SRichard Pospesel #define BYD_PACKET_REGION_SCROLL_LEFT		0xcb
2172d5f5611SRichard Pospesel #define BYD_PACKET_RIGHT_CORNER_CLICK		0xd2
2182d5f5611SRichard Pospesel #define BYD_PACKET_LEFT_CORNER_CLICK		0x2e
2192d5f5611SRichard Pospesel #define BYD_PACKET_LEFT_AND_RIGHT_CORNER_CLICK	0x2f
2202d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_RIGHT		0x37
2212d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_DOWN		0x30
2222d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_UP		0xd0
2232d5f5611SRichard Pospesel #define BYD_PACKET_ONTO_PAD_SWIPE_LEFT		0xc9
2242d5f5611SRichard Pospesel 
2252d5f5611SRichard Pospesel struct byd_data {
2262d5f5611SRichard Pospesel 	struct timer_list timer;
227ce23cbc8SKees Cook 	struct psmouse *psmouse;
2282d5f5611SRichard Pospesel 	s32 abs_x;
2292d5f5611SRichard Pospesel 	s32 abs_y;
2302d5f5611SRichard Pospesel 	typeof(jiffies) last_touch_time;
2312d5f5611SRichard Pospesel 	bool btn_left;
2322d5f5611SRichard Pospesel 	bool btn_right;
2332d5f5611SRichard Pospesel 	bool touch;
2342d5f5611SRichard Pospesel };
2352d5f5611SRichard Pospesel 
byd_report_input(struct psmouse * psmouse)2362d5f5611SRichard Pospesel static void byd_report_input(struct psmouse *psmouse)
23798ee3771SChris Diamand {
2382d5f5611SRichard Pospesel 	struct byd_data *priv = psmouse->private;
2392d5f5611SRichard Pospesel 	struct input_dev *dev = psmouse->dev;
24098ee3771SChris Diamand 
2412d5f5611SRichard Pospesel 	input_report_key(dev, BTN_TOUCH, priv->touch);
2422d5f5611SRichard Pospesel 	input_report_key(dev, BTN_TOOL_FINGER, priv->touch);
24398ee3771SChris Diamand 
2442d5f5611SRichard Pospesel 	input_report_abs(dev, ABS_X, priv->abs_x);
2452d5f5611SRichard Pospesel 	input_report_abs(dev, ABS_Y, priv->abs_y);
2462d5f5611SRichard Pospesel 	input_report_key(dev, BTN_LEFT, priv->btn_left);
2472d5f5611SRichard Pospesel 	input_report_key(dev, BTN_RIGHT, priv->btn_right);
24898ee3771SChris Diamand 
2492d5f5611SRichard Pospesel 	input_sync(dev);
25098ee3771SChris Diamand }
25198ee3771SChris Diamand 
byd_clear_touch(struct timer_list * t)252ce23cbc8SKees Cook static void byd_clear_touch(struct timer_list *t)
2532d5f5611SRichard Pospesel {
254ce23cbc8SKees Cook 	struct byd_data *priv = from_timer(priv, t, timer);
255ce23cbc8SKees Cook 	struct psmouse *psmouse = priv->psmouse;
2562d5f5611SRichard Pospesel 
2572d5f5611SRichard Pospesel 	serio_pause_rx(psmouse->ps2dev.serio);
2582d5f5611SRichard Pospesel 	priv->touch = false;
2592d5f5611SRichard Pospesel 
2602d5f5611SRichard Pospesel 	byd_report_input(psmouse);
2612d5f5611SRichard Pospesel 
2622d5f5611SRichard Pospesel 	serio_continue_rx(psmouse->ps2dev.serio);
2632d5f5611SRichard Pospesel 
2642d5f5611SRichard Pospesel 	/*
2652d5f5611SRichard Pospesel 	 * Move cursor back to center of pad when we lose touch - this
2662d5f5611SRichard Pospesel 	 * specifically improves user experience when moving cursor with one
2672d5f5611SRichard Pospesel 	 * finger, and pressing a button with another.
2682d5f5611SRichard Pospesel 	 */
2692d5f5611SRichard Pospesel 	priv->abs_x = BYD_PAD_WIDTH / 2;
2702d5f5611SRichard Pospesel 	priv->abs_y = BYD_PAD_HEIGHT / 2;
27198ee3771SChris Diamand }
27298ee3771SChris Diamand 
byd_process_byte(struct psmouse * psmouse)27398ee3771SChris Diamand static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
27498ee3771SChris Diamand {
2752d5f5611SRichard Pospesel 	struct byd_data *priv = psmouse->private;
27698ee3771SChris Diamand 	u8 *pkt = psmouse->packet;
27798ee3771SChris Diamand 
27898ee3771SChris Diamand 	if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
27998ee3771SChris Diamand 		psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
28098ee3771SChris Diamand 			     pkt[0]);
28198ee3771SChris Diamand 		return PSMOUSE_BAD_DATA;
28298ee3771SChris Diamand 	}
28398ee3771SChris Diamand 
28498ee3771SChris Diamand 	if (psmouse->pktcnt < psmouse->pktsize)
28598ee3771SChris Diamand 		return PSMOUSE_GOOD_DATA;
28698ee3771SChris Diamand 
28798ee3771SChris Diamand 	/* Otherwise, a full packet has been received */
28898ee3771SChris Diamand 	switch (pkt[3]) {
2892d5f5611SRichard Pospesel 	case BYD_PACKET_ABSOLUTE:
2902d5f5611SRichard Pospesel 		/* Only use absolute packets for the start of movement. */
2912d5f5611SRichard Pospesel 		if (!priv->touch) {
2922d5f5611SRichard Pospesel 			/* needed to detect tap */
2932d5f5611SRichard Pospesel 			typeof(jiffies) tap_time =
2942d5f5611SRichard Pospesel 				priv->last_touch_time + BYD_TOUCH_TIMEOUT;
2952d5f5611SRichard Pospesel 			priv->touch = time_after(jiffies, tap_time);
2962d5f5611SRichard Pospesel 
2972d5f5611SRichard Pospesel 			/* init abs position */
2982d5f5611SRichard Pospesel 			priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256);
2992d5f5611SRichard Pospesel 			priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256);
3002d5f5611SRichard Pospesel 		}
3012d5f5611SRichard Pospesel 		break;
3022d5f5611SRichard Pospesel 	case BYD_PACKET_RELATIVE: {
30398ee3771SChris Diamand 		/* Standard packet */
30498ee3771SChris Diamand 		/* Sign-extend if a sign bit is set. */
3052d5f5611SRichard Pospesel 		u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
3062d5f5611SRichard Pospesel 		u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
3072d5f5611SRichard Pospesel 		s32 dx = signx | (int) pkt[1];
3082d5f5611SRichard Pospesel 		s32 dy = signy | (int) pkt[2];
30998ee3771SChris Diamand 
3102d5f5611SRichard Pospesel 		/* Update position based on velocity */
3112d5f5611SRichard Pospesel 		priv->abs_x += dx * BYD_DT;
3122d5f5611SRichard Pospesel 		priv->abs_y -= dy * BYD_DT;
31398ee3771SChris Diamand 
3142d5f5611SRichard Pospesel 		priv->touch = true;
31598ee3771SChris Diamand 		break;
31698ee3771SChris Diamand 	}
31798ee3771SChris Diamand 	default:
31898ee3771SChris Diamand 		psmouse_warn(psmouse,
31998ee3771SChris Diamand 			     "Unrecognized Z: pkt = %02x %02x %02x %02x\n",
32098ee3771SChris Diamand 			     psmouse->packet[0], psmouse->packet[1],
32198ee3771SChris Diamand 			     psmouse->packet[2], psmouse->packet[3]);
32298ee3771SChris Diamand 		return PSMOUSE_BAD_DATA;
32398ee3771SChris Diamand 	}
32498ee3771SChris Diamand 
3252d5f5611SRichard Pospesel 	priv->btn_left = pkt[0] & PS2_LEFT;
3262d5f5611SRichard Pospesel 	priv->btn_right = pkt[0] & PS2_RIGHT;
3272d5f5611SRichard Pospesel 
3282d5f5611SRichard Pospesel 	byd_report_input(psmouse);
3292d5f5611SRichard Pospesel 
3302d5f5611SRichard Pospesel 	/* Reset time since last touch. */
3312d5f5611SRichard Pospesel 	if (priv->touch) {
3322d5f5611SRichard Pospesel 		priv->last_touch_time = jiffies;
3332d5f5611SRichard Pospesel 		mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT);
3342d5f5611SRichard Pospesel 	}
33598ee3771SChris Diamand 
33698ee3771SChris Diamand 	return PSMOUSE_FULL_PACKET;
33798ee3771SChris Diamand }
33898ee3771SChris Diamand 
byd_reset_touchpad(struct psmouse * psmouse)3392d5f5611SRichard Pospesel static int byd_reset_touchpad(struct psmouse *psmouse)
34098ee3771SChris Diamand {
34198ee3771SChris Diamand 	struct ps2dev *ps2dev = &psmouse->ps2dev;
34298ee3771SChris Diamand 	u8 param[4];
3432d5f5611SRichard Pospesel 	size_t i;
3442d5f5611SRichard Pospesel 
34508d6ac9eSColin Ian King 	static const struct {
34698ee3771SChris Diamand 		u16 command;
34798ee3771SChris Diamand 		u8 arg;
34898ee3771SChris Diamand 	} seq[] = {
3492d5f5611SRichard Pospesel 		/*
3502d5f5611SRichard Pospesel 		 * Intellimouse initialization sequence, to get 4-byte instead
3512d5f5611SRichard Pospesel 		 * of 3-byte packets.
3522d5f5611SRichard Pospesel 		 */
35398ee3771SChris Diamand 		{ PSMOUSE_CMD_SETRATE, 0xC8 },
35498ee3771SChris Diamand 		{ PSMOUSE_CMD_SETRATE, 0x64 },
35598ee3771SChris Diamand 		{ PSMOUSE_CMD_SETRATE, 0x50 },
35698ee3771SChris Diamand 		{ PSMOUSE_CMD_GETID, 0 },
3572d5f5611SRichard Pospesel 		{ PSMOUSE_CMD_ENABLE, 0 },
3582d5f5611SRichard Pospesel 		/*
3592d5f5611SRichard Pospesel 		 * BYD-specific initialization, which enables absolute mode and
3602d5f5611SRichard Pospesel 		 * (if desired), the touchpad's built-in gesture detection.
3612d5f5611SRichard Pospesel 		 */
3622d5f5611SRichard Pospesel 		{ 0x10E2, 0x00 },
3632d5f5611SRichard Pospesel 		{ 0x10E0, 0x02 },
3642d5f5611SRichard Pospesel 		/* The touchpad should reply with 4 seemingly-random bytes */
3652d5f5611SRichard Pospesel 		{ 0x14E0, 0x01 },
3662d5f5611SRichard Pospesel 		/* Pairs of parameters and values. */
3672d5f5611SRichard Pospesel 		{ BYD_CMD_SET_HANDEDNESS, 0x01 },
3682d5f5611SRichard Pospesel 		{ BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 },
3692d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TAP, 0x02 },
3702d5f5611SRichard Pospesel 		{ BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 },
3712d5f5611SRichard Pospesel 		{ BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 },
3722d5f5611SRichard Pospesel 		{ BYD_CMD_SET_EDGE_MOTION, 0x01 },
3732d5f5611SRichard Pospesel 		{ BYD_CMD_SET_PALM_CHECK, 0x00 },
3742d5f5611SRichard Pospesel 		{ BYD_CMD_SET_MULTITOUCH, 0x02 },
3752d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 },
3762d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 },
3772d5f5611SRichard Pospesel 		{ BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 },
3782d5f5611SRichard Pospesel 		{ BYD_CMD_SET_TOP_EDGE_REGION, 0x00 },
3792d5f5611SRichard Pospesel 		{ BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 },
3802d5f5611SRichard Pospesel 		{ BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 },
3812d5f5611SRichard Pospesel 		{ BYD_CMD_SET_ABSOLUTE_MODE, 0x02 },
3822d5f5611SRichard Pospesel 		/* Finalize initialization. */
3832d5f5611SRichard Pospesel 		{ 0x10E0, 0x00 },
3842d5f5611SRichard Pospesel 		{ 0x10E2, 0x01 },
38598ee3771SChris Diamand 	};
38698ee3771SChris Diamand 
38798ee3771SChris Diamand 	for (i = 0; i < ARRAY_SIZE(seq); ++i) {
3882d5f5611SRichard Pospesel 		memset(param, 0, sizeof(param));
38998ee3771SChris Diamand 		param[0] = seq[i].arg;
39098ee3771SChris Diamand 		if (ps2_command(ps2dev, param, seq[i].command))
3912d5f5611SRichard Pospesel 			return -EIO;
39298ee3771SChris Diamand 	}
39398ee3771SChris Diamand 
3942d5f5611SRichard Pospesel 	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
39598ee3771SChris Diamand 	return 0;
39698ee3771SChris Diamand }
39798ee3771SChris Diamand 
byd_reconnect(struct psmouse * psmouse)39898ee3771SChris Diamand static int byd_reconnect(struct psmouse *psmouse)
39998ee3771SChris Diamand {
40098ee3771SChris Diamand 	int retry = 0, error = 0;
40198ee3771SChris Diamand 
40298ee3771SChris Diamand 	psmouse_dbg(psmouse, "Reconnect\n");
40398ee3771SChris Diamand 	do {
40498ee3771SChris Diamand 		psmouse_reset(psmouse);
40598ee3771SChris Diamand 		if (retry)
40698ee3771SChris Diamand 			ssleep(1);
40798ee3771SChris Diamand 		error = byd_detect(psmouse, 0);
40898ee3771SChris Diamand 	} while (error && ++retry < 3);
40998ee3771SChris Diamand 
41098ee3771SChris Diamand 	if (error)
41198ee3771SChris Diamand 		return error;
41298ee3771SChris Diamand 
41398ee3771SChris Diamand 	psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
41498ee3771SChris Diamand 
41598ee3771SChris Diamand 	error = byd_reset_touchpad(psmouse);
41698ee3771SChris Diamand 	if (error) {
41798ee3771SChris Diamand 		psmouse_err(psmouse, "Unable to initialize device\n");
41898ee3771SChris Diamand 		return error;
41998ee3771SChris Diamand 	}
42098ee3771SChris Diamand 
42198ee3771SChris Diamand 	return 0;
42298ee3771SChris Diamand }
42398ee3771SChris Diamand 
byd_disconnect(struct psmouse * psmouse)4242d5f5611SRichard Pospesel static void byd_disconnect(struct psmouse *psmouse)
4252d5f5611SRichard Pospesel {
4262d5f5611SRichard Pospesel 	struct byd_data *priv = psmouse->private;
4272d5f5611SRichard Pospesel 
4282d5f5611SRichard Pospesel 	if (priv) {
4292d5f5611SRichard Pospesel 		del_timer(&priv->timer);
4302d5f5611SRichard Pospesel 		kfree(psmouse->private);
4312d5f5611SRichard Pospesel 		psmouse->private = NULL;
4322d5f5611SRichard Pospesel 	}
4332d5f5611SRichard Pospesel }
4342d5f5611SRichard Pospesel 
byd_detect(struct psmouse * psmouse,bool set_properties)4352d5f5611SRichard Pospesel int byd_detect(struct psmouse *psmouse, bool set_properties)
4362d5f5611SRichard Pospesel {
4372d5f5611SRichard Pospesel 	struct ps2dev *ps2dev = &psmouse->ps2dev;
4382d5f5611SRichard Pospesel 	u8 param[4] = {0x03, 0x00, 0x00, 0x00};
4392d5f5611SRichard Pospesel 
4402d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4412d5f5611SRichard Pospesel 		return -1;
4422d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4432d5f5611SRichard Pospesel 		return -1;
4442d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4452d5f5611SRichard Pospesel 		return -1;
4462d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
4472d5f5611SRichard Pospesel 		return -1;
4482d5f5611SRichard Pospesel 	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
4492d5f5611SRichard Pospesel 		return -1;
4502d5f5611SRichard Pospesel 
4512d5f5611SRichard Pospesel 	if (param[1] != 0x03 || param[2] != 0x64)
4522d5f5611SRichard Pospesel 		return -ENODEV;
4532d5f5611SRichard Pospesel 
4542d5f5611SRichard Pospesel 	psmouse_dbg(psmouse, "BYD touchpad detected\n");
4552d5f5611SRichard Pospesel 
4562d5f5611SRichard Pospesel 	if (set_properties) {
4572d5f5611SRichard Pospesel 		psmouse->vendor = "BYD";
4582d5f5611SRichard Pospesel 		psmouse->name = "TouchPad";
4592d5f5611SRichard Pospesel 	}
4602d5f5611SRichard Pospesel 
4612d5f5611SRichard Pospesel 	return 0;
4622d5f5611SRichard Pospesel }
4632d5f5611SRichard Pospesel 
byd_init(struct psmouse * psmouse)46498ee3771SChris Diamand int byd_init(struct psmouse *psmouse)
46598ee3771SChris Diamand {
46698ee3771SChris Diamand 	struct input_dev *dev = psmouse->dev;
4672d5f5611SRichard Pospesel 	struct byd_data *priv;
46898ee3771SChris Diamand 
46998ee3771SChris Diamand 	if (psmouse_reset(psmouse))
47098ee3771SChris Diamand 		return -EIO;
47198ee3771SChris Diamand 
47298ee3771SChris Diamand 	if (byd_reset_touchpad(psmouse))
47398ee3771SChris Diamand 		return -EIO;
47498ee3771SChris Diamand 
4752d5f5611SRichard Pospesel 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
4762d5f5611SRichard Pospesel 	if (!priv)
4772d5f5611SRichard Pospesel 		return -ENOMEM;
4782d5f5611SRichard Pospesel 
479ce23cbc8SKees Cook 	priv->psmouse = psmouse;
480ce23cbc8SKees Cook 	timer_setup(&priv->timer, byd_clear_touch, 0);
4812d5f5611SRichard Pospesel 
4822d5f5611SRichard Pospesel 	psmouse->private = priv;
4832d5f5611SRichard Pospesel 	psmouse->disconnect = byd_disconnect;
48498ee3771SChris Diamand 	psmouse->reconnect = byd_reconnect;
48598ee3771SChris Diamand 	psmouse->protocol_handler = byd_process_byte;
48698ee3771SChris Diamand 	psmouse->pktsize = 4;
48798ee3771SChris Diamand 	psmouse->resync_time = 0;
48898ee3771SChris Diamand 
4892d5f5611SRichard Pospesel 	__set_bit(INPUT_PROP_POINTER, dev->propbit);
4902d5f5611SRichard Pospesel 	/* Touchpad */
4912d5f5611SRichard Pospesel 	__set_bit(BTN_TOUCH, dev->keybit);
4922d5f5611SRichard Pospesel 	__set_bit(BTN_TOOL_FINGER, dev->keybit);
4932d5f5611SRichard Pospesel 	/* Buttons */
4942d5f5611SRichard Pospesel 	__set_bit(BTN_LEFT, dev->keybit);
4952d5f5611SRichard Pospesel 	__set_bit(BTN_RIGHT, dev->keybit);
4962d5f5611SRichard Pospesel 	__clear_bit(BTN_MIDDLE, dev->keybit);
4972d5f5611SRichard Pospesel 
4982d5f5611SRichard Pospesel 	/* Absolute position */
4992d5f5611SRichard Pospesel 	__set_bit(EV_ABS, dev->evbit);
5002d5f5611SRichard Pospesel 	input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0);
5012d5f5611SRichard Pospesel 	input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0);
5022d5f5611SRichard Pospesel 	input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION);
5032d5f5611SRichard Pospesel 	input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION);
5042d5f5611SRichard Pospesel 	/* No relative support */
5052d5f5611SRichard Pospesel 	__clear_bit(EV_REL, dev->evbit);
5062d5f5611SRichard Pospesel 	__clear_bit(REL_X, dev->relbit);
5072d5f5611SRichard Pospesel 	__clear_bit(REL_Y, dev->relbit);
50898ee3771SChris Diamand 
50998ee3771SChris Diamand 	return 0;
51098ee3771SChris Diamand }
511