2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-16 15:20:36 -07:00
/*
* Synaptics TouchPad PS/2 mouse driver
*
* 2003 Dmitry Torokhov <dtor@mail.ru>
* Added support for pass-through port. Special thanks to Peter Berg Larsen
* for explaining various Synaptics quirks.
*
* 2003 Peter Osterlund <petero2@telia.com>
* Ported to 2.5 input device infrastructure.
*
* Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
* start merging tpconfig and gpm code to a xfree-input module
* adding some changes and extensions (ex. 3rd and 4th button)
*
* Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
* Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
* code for the special synaptics commands (from the tpconfig-source)
*
* Trademarks are the property of their respective owners.
*/
# include <linux/module.h>
2011-12-12 00:05:53 -08:00
# include <linux/delay.h>
2009-12-03 23:21:14 -08:00
# include <linux/dmi.h>
2010-12-21 18:11:25 +01:00
# include <linux/input/mt.h>
2005-04-16 15:20:36 -07:00
# include <linux/serio.h>
# include <linux/libps2.h>
2017-03-02 14:13:53 -08:00
# include <linux/rmi.h>
# include <linux/i2c.h>
2010-03-24 17:04:11 +09:00
# include <linux/slab.h>
2005-04-16 15:20:36 -07:00
# include "psmouse.h"
# include "synaptics.h"
/*
* The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
* section 2.3.2, which says that they should be valid regardless of the
* actual size of the sensor.
2010-05-10 23:06:52 -07:00
* Note that newer firmware allows querying device for maximum useable
* coordinates.
2005-04-16 15:20:36 -07:00
*/
2012-07-24 23:54:11 -07:00
# define XMIN 0
# define XMAX 6143
# define YMIN 0
# define YMAX 6143
2005-04-16 15:20:36 -07:00
# define XMIN_NOMINAL 1472
# define XMAX_NOMINAL 5472
# define YMIN_NOMINAL 1408
# define YMAX_NOMINAL 4448
2012-07-24 23:54:11 -07:00
/* Size in bits of absolute position values reported by the hardware */
# define ABS_POS_BITS 13
/*
2012-09-28 10:29:21 -07:00
* These values should represent the absolute maximum value that will
* be reported for a positive position value. Some Synaptics firmware
* uses this value to indicate a finger near the edge of the touchpad
* whose precise position cannot be determined.
*
* At least one touchpad is known to report positions in excess of this
* value which are actually negative values truncated to the 13-bit
* reporting range. These values have never been observed to be lower
* than 8184 (i.e. -8), so we treat all values greater than 8176 as
* negative and any other value as positive.
2012-07-24 23:54:11 -07:00
*/
2012-09-28 10:29:21 -07:00
# define X_MAX_POSITIVE 8176
# define Y_MAX_POSITIVE 8176
2005-04-16 15:20:36 -07:00
2015-04-05 13:44:12 -07:00
/* maximum ABS_MT_POSITION displacement (in mm) */
# define DMAX 10
2007-03-10 01:39:54 -05:00
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
****************************************************************************/
2005-04-16 15:20:36 -07:00
/*
* Set the synaptics touchpad mode byte by special commands
*/
2017-03-24 11:20:38 -07:00
static int synaptics_mode_cmd ( struct psmouse * psmouse , u8 mode )
2005-04-16 15:20:36 -07:00
{
2017-03-24 11:20:38 -07:00
u8 param [ 1 ] ;
2017-03-23 18:38:14 -07:00
int error ;
2018-01-02 12:03:02 -08:00
error = ps2_sliced_command ( & psmouse - > ps2dev , mode ) ;
2017-03-23 18:38:14 -07:00
if ( error )
return error ;
2005-04-16 15:20:36 -07:00
param [ 0 ] = SYN_PS_SET_MODE2 ;
2017-03-23 18:38:14 -07:00
error = ps2_command ( & psmouse - > ps2dev , param , PSMOUSE_CMD_SETRATE ) ;
if ( error )
return error ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-09-09 19:13:20 -07:00
int synaptics_detect ( struct psmouse * psmouse , bool set_properties )
2007-03-10 01:39:54 -05:00
{
struct ps2dev * ps2dev = & psmouse - > ps2dev ;
2018-10-16 17:07:35 -07:00
u8 param [ 4 ] = { 0 } ;
2007-03-10 01:39:54 -05:00
ps2_command ( ps2dev , param , PSMOUSE_CMD_SETRES ) ;
ps2_command ( ps2dev , param , PSMOUSE_CMD_SETRES ) ;
ps2_command ( ps2dev , param , PSMOUSE_CMD_SETRES ) ;
ps2_command ( ps2dev , param , PSMOUSE_CMD_SETRES ) ;
ps2_command ( ps2dev , param , PSMOUSE_CMD_GETINFO ) ;
if ( param [ 1 ] ! = 0x47 )
return - ENODEV ;
if ( set_properties ) {
psmouse - > vendor = " Synaptics " ;
psmouse - > name = " TouchPad " ;
}
return 0 ;
}
void synaptics_reset ( struct psmouse * psmouse )
{
/* reset touchpad back to relative mode, gestures enabled */
synaptics_mode_cmd ( psmouse , 0 ) ;
}
2017-03-02 14:13:53 -08:00
# if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
2014-05-19 22:54:09 -07:00
2014-04-19 22:26:41 -07:00
/* This list has been kindly provided by Synaptics. */
static const char * const topbuttonpad_pnp_ids [ ] = {
" LEN0017 " ,
" LEN0018 " ,
" LEN0019 " ,
" LEN0023 " ,
" LEN002A " ,
" LEN002B " ,
" LEN002C " ,
" LEN002D " ,
" LEN002E " ,
" LEN0033 " , /* Helix */
2014-05-19 22:54:09 -07:00
" LEN0034 " , /* T431s, L440, L540, T540, W540, X1 Carbon 2nd */
2014-04-19 22:26:41 -07:00
" LEN0035 " , /* X240 */
" LEN0036 " , /* T440 */
2015-01-19 16:29:25 -08:00
" LEN0037 " , /* X1 Carbon 2nd */
2014-04-19 22:26:41 -07:00
" LEN0038 " ,
2014-11-06 09:27:11 -08:00
" LEN0039 " , /* T440s */
2014-04-19 22:26:41 -07:00
" LEN0041 " ,
" LEN0042 " , /* Yoga */
" LEN0045 " ,
" LEN0047 " ,
2015-06-08 10:17:32 -07:00
" LEN2000 " , /* S540 */
2014-05-19 22:54:09 -07:00
" LEN2001 " , /* Edge E431 */
2014-07-14 17:12:21 -07:00
" LEN2002 " , /* Edge E531 */
2014-04-19 22:26:41 -07:00
" LEN2003 " ,
" LEN2004 " , /* L440 */
" LEN2005 " ,
2015-03-23 10:33:07 -07:00
" LEN2006 " , /* Edge E440/E540 */
2014-04-19 22:26:41 -07:00
" LEN2007 " ,
" LEN2008 " ,
" LEN2009 " ,
" LEN200A " ,
" LEN200B " ,
NULL
} ;
2007-03-10 01:39:54 -05:00
2017-03-02 14:13:53 -08:00
static const char * const smbus_pnp_ids [ ] = {
/* all of the topbuttonpad_pnp_ids are valid, we just add some extras */
" LEN0048 " , /* X1 Carbon 3 */
" LEN0046 " , /* X250 */
2020-02-13 17:07:47 -08:00
" LEN0049 " , /* Yoga 11e */
2017-03-02 14:13:53 -08:00
" LEN004a " , /* W541 */
2018-11-24 23:28:10 -08:00
" LEN005b " , /* P50 */
2018-12-08 20:13:35 -08:00
" LEN005e " , /* T560 */
2020-02-13 16:59:15 -08:00
" LEN006c " , /* T470s */
2020-05-26 23:03:13 -07:00
" LEN007a " , /* T470s */
2018-05-22 17:16:08 -07:00
" LEN0071 " , /* T480 */
2017-08-18 12:08:13 -07:00
" LEN0072 " , /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */
2018-02-03 11:46:15 -08:00
" LEN0073 " , /* X1 Carbon G5 (Elantech) */
2019-11-22 16:17:08 -08:00
" LEN0091 " , /* X1 Carbon 6 */
2018-02-03 11:49:22 -08:00
" LEN0092 " , /* X1 Carbon 6 */
2019-07-01 00:47:48 -07:00
" LEN0093 " , /* T480 */
2018-02-03 11:49:22 -08:00
" LEN0096 " , /* X280 */
2018-05-22 17:16:08 -07:00
" LEN0097 " , /* X280 -> ALPS trackpoint */
2020-10-04 19:49:08 -07:00
" LEN0099 " , /* X1 Extreme Gen 1 / P1 Gen 1 */
2019-07-11 23:42:03 -07:00
" LEN009b " , /* T580 */
2020-10-04 19:49:08 -07:00
" LEN0402 " , /* X1 Extreme Gen 2 / P1 Gen 2 */
2022-09-14 05:34:49 -07:00
" LEN040f " , /* P1 Gen 3 */
2017-03-02 14:13:53 -08:00
" LEN200f " , /* T450s */
2020-02-13 17:06:10 -08:00
" LEN2044 " , /* L470 */
2019-06-12 14:59:46 -07:00
" LEN2054 " , /* E480 */
" LEN2055 " , /* E580 */
2020-10-05 10:13:18 -07:00
" LEN2068 " , /* T14 Gen 1 */
2018-12-21 01:04:26 -08:00
" SYN3052 " , /* HP EliteBook 840 G4 */
2018-12-03 11:26:03 -08:00
" SYN3221 " , /* HP 15-ay000 */
2019-07-12 11:37:17 -07:00
" SYN323d " , /* HP Spectre X360 13-w013dx */
2020-03-07 14:16:31 -08:00
" SYN3257 " , /* HP Envy 13-ad105ng */
2014-12-29 14:43:44 -08:00
NULL
} ;
2017-05-26 16:21:36 -07:00
static const char * const forcepad_pnp_ids [ ] = {
" SYN300D " ,
" SYN3014 " ,
NULL
} ;
2007-03-10 01:39:54 -05:00
/*
2019-08-09 09:45:27 -07:00
* Send a command to the synaptics touchpad by special commands
2007-03-10 01:39:54 -05:00
*/
2017-03-24 11:20:38 -07:00
static int synaptics_send_cmd ( struct psmouse * psmouse , u8 cmd , u8 * param )
2007-03-10 01:39:54 -05:00
{
2017-03-02 14:13:53 -08:00
int error ;
2018-01-02 12:03:02 -08:00
error = ps2_sliced_command ( & psmouse - > ps2dev , cmd ) ;
2017-03-02 14:13:53 -08:00
if ( error )
return error ;
error = ps2_command ( & psmouse - > ps2dev , param , PSMOUSE_CMD_GETINFO ) ;
if ( error )
return error ;
2007-03-10 01:39:54 -05:00
return 0 ;
}
2017-03-23 17:40:57 -07:00
static int synaptics_query_int ( struct psmouse * psmouse , u8 query_cmd , u32 * val )
{
int error ;
union {
__be32 be_val ;
char buf [ 4 ] ;
} resp = { 0 } ;
error = synaptics_send_cmd ( psmouse , query_cmd , resp . buf + 1 ) ;
if ( error )
return error ;
* val = be32_to_cpu ( resp . be_val ) ;
return 0 ;
}
2017-03-02 14:13:53 -08:00
/*
* Identify Touchpad
* See also the SYN_ID_* macros
*/
static int synaptics_identify ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
{
int error ;
2017-03-23 17:40:57 -07:00
error = synaptics_query_int ( psmouse , SYN_QUE_IDENTIFY , & info - > identity ) ;
2017-03-02 14:13:53 -08:00
if ( error )
return error ;
return SYN_ID_IS_SYNAPTICS ( info - > identity ) ? 0 : - ENXIO ;
}
2005-04-16 15:20:36 -07:00
/*
* Read the model-id bytes from the touchpad
* see also SYN_MODEL_* macros
*/
2017-03-05 15:51:33 -08:00
static int synaptics_model_id ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
2005-04-16 15:20:36 -07:00
{
2017-03-23 17:40:57 -07:00
return synaptics_query_int ( psmouse , SYN_QUE_MODEL , & info - > model_id ) ;
2005-04-16 15:20:36 -07:00
}
2017-03-02 14:13:53 -08:00
/*
* Read the firmware id from the touchpad
*/
static int synaptics_firmware_id ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
{
2017-03-23 17:40:57 -07:00
return synaptics_query_int ( psmouse , SYN_QUE_FIRMWARE_ID ,
& info - > firmware_id ) ;
2015-03-08 22:34:03 -07:00
}
2012-07-07 18:08:51 -07:00
/*
2015-03-08 22:34:03 -07:00
* Read the board id and the "More Extended Queries" from the touchpad
2012-07-07 18:08:51 -07:00
* The board id is encoded in the "QUERY MODES" response
*/
2017-03-05 15:51:33 -08:00
static int synaptics_query_modes ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
2012-07-07 18:08:51 -07:00
{
2017-03-24 11:20:38 -07:00
u8 bid [ 3 ] ;
2017-03-05 15:51:33 -08:00
int error ;
2012-07-07 18:08:51 -07:00
2015-03-08 22:33:36 -07:00
/* firmwares prior 7.5 have no board_id encoded */
2017-03-05 15:51:33 -08:00
if ( SYN_ID_FULL ( info - > identity ) < 0x705 )
2015-03-08 22:33:36 -07:00
return 0 ;
2017-03-05 15:51:33 -08:00
error = synaptics_send_cmd ( psmouse , SYN_QUE_MODES , bid ) ;
if ( error )
return error ;
info - > board_id = ( ( bid [ 0 ] & 0xfc ) < < 6 ) | bid [ 1 ] ;
2015-03-08 22:34:03 -07:00
if ( SYN_MEXT_CAP_BIT ( bid [ 0 ] ) )
2017-03-23 17:40:57 -07:00
return synaptics_query_int ( psmouse , SYN_QUE_MEXT_CAPAB_10 ,
& info - > ext_cap_10 ) ;
2015-03-08 22:34:03 -07:00
2012-07-07 18:08:51 -07:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
/*
* Read the capability-bits from the touchpad
* see also the SYN_CAP_* macros
*/
2017-03-05 15:51:33 -08:00
static int synaptics_capability ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
2005-04-16 15:20:36 -07:00
{
2017-03-05 15:51:33 -08:00
int error ;
2005-04-16 15:20:36 -07:00
2017-03-23 17:40:57 -07:00
error = synaptics_query_int ( psmouse , SYN_QUE_CAPABILITIES ,
& info - > capabilities ) ;
2017-03-05 15:51:33 -08:00
if ( error )
return error ;
info - > ext_cap = info - > ext_cap_0c = 0 ;
2010-04-19 10:37:21 -07:00
2010-07-21 00:01:19 -07:00
/*
* Older firmwares had submodel ID fixed to 0x47
*/
2017-03-05 15:51:33 -08:00
if ( SYN_ID_FULL ( info - > identity ) < 0x705 & &
SYN_CAP_SUBMODEL_ID ( info - > capabilities ) ! = 0x47 ) {
return - ENXIO ;
2010-07-21 00:01:19 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* Unless capExtended is set the rest of the flags should be ignored
*/
2017-03-05 15:51:33 -08:00
if ( ! SYN_CAP_EXTENDED ( info - > capabilities ) )
info - > capabilities = 0 ;
2005-04-16 15:20:36 -07:00
2017-03-05 15:51:33 -08:00
if ( SYN_EXT_CAP_REQUESTS ( info - > capabilities ) > = 1 ) {
2017-03-23 17:40:57 -07:00
error = synaptics_query_int ( psmouse , SYN_QUE_EXT_CAPAB ,
& info - > ext_cap ) ;
if ( error ) {
2011-10-10 18:27:03 -07:00
psmouse_warn ( psmouse ,
" device claims to have extended capabilities, but I'm not able to read them. \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
/*
* if nExtBtn is greater than 8 it should be considered
* invalid and treated as 0
*/
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_MULTI_BUTTON_NO ( info - > ext_cap ) > 8 )
2017-03-23 17:40:57 -07:00
info - > ext_cap & = ~ SYN_CAP_MB_MASK ;
2005-04-16 15:20:36 -07:00
}
}
2010-04-19 10:37:21 -07:00
2017-03-05 15:51:33 -08:00
if ( SYN_EXT_CAP_REQUESTS ( info - > capabilities ) > = 4 ) {
2017-03-23 17:40:57 -07:00
error = synaptics_query_int ( psmouse , SYN_QUE_EXT_CAPAB_0C ,
& info - > ext_cap_0c ) ;
2017-03-05 15:51:33 -08:00
if ( error )
2011-10-10 18:27:03 -07:00
psmouse_warn ( psmouse ,
" device claims to have extended capability 0x0c, but I'm not able to read it. \n " ) ;
2010-04-19 10:37:21 -07:00
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-06-10 23:27:24 -07:00
/*
2010-05-10 23:06:52 -07:00
* Read touchpad resolution and maximum reported coordinates
2009-06-10 23:27:24 -07:00
* Resolution is left zero if touchpad does not support the query
*/
2017-03-05 15:51:33 -08:00
static int synaptics_resolution ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
2009-06-10 23:27:24 -07:00
{
2017-03-24 11:20:38 -07:00
u8 resp [ 3 ] ;
2017-03-05 15:51:33 -08:00
int error ;
2009-06-10 23:27:24 -07:00
2017-03-05 15:51:33 -08:00
if ( SYN_ID_MAJOR ( info - > identity ) < 4 )
2010-07-14 09:32:46 -07:00
return 0 ;
2009-06-10 23:27:24 -07:00
2017-03-05 15:51:33 -08:00
error = synaptics_send_cmd ( psmouse , SYN_QUE_RESOLUTION , resp ) ;
if ( ! error ) {
2011-07-09 12:32:56 -07:00
if ( resp [ 0 ] ! = 0 & & ( resp [ 1 ] & 0x80 ) & & resp [ 2 ] ! = 0 ) {
2017-03-05 15:51:33 -08:00
info - > x_res = resp [ 0 ] ; /* x resolution in units/mm */
info - > y_res = resp [ 2 ] ; /* y resolution in units/mm */
2010-05-10 23:06:52 -07:00
}
}
2009-06-10 23:27:24 -07:00
2017-03-05 15:51:33 -08:00
if ( SYN_EXT_CAP_REQUESTS ( info - > capabilities ) > = 5 & &
SYN_CAP_MAX_DIMENSIONS ( info - > ext_cap_0c ) ) {
error = synaptics_send_cmd ( psmouse ,
SYN_QUE_EXT_MAX_COORDS , resp ) ;
if ( error ) {
2011-10-10 18:27:03 -07:00
psmouse_warn ( psmouse ,
" device claims to have max coordinates query, but I'm not able to read it. \n " ) ;
2010-05-10 23:06:52 -07:00
} else {
2017-03-05 15:51:33 -08:00
info - > x_max = ( resp [ 0 ] < < 5 ) | ( ( resp [ 1 ] & 0x0f ) < < 1 ) ;
info - > y_max = ( resp [ 2 ] < < 5 ) | ( ( resp [ 1 ] & 0xf0 ) > > 3 ) ;
2015-03-08 22:28:29 -07:00
psmouse_info ( psmouse ,
" queried max coordinates: x [..%d], y [..%d] \n " ,
2017-03-05 15:51:33 -08:00
info - > x_max , info - > y_max ) ;
2011-07-09 12:32:56 -07:00
}
}
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_MIN_DIMENSIONS ( info - > ext_cap_0c ) & &
( SYN_EXT_CAP_REQUESTS ( info - > capabilities ) > = 7 | |
2015-03-08 22:28:40 -07:00
/*
* Firmware v8.1 does not report proper number of extended
* capabilities, but has been proven to report correct min
* coordinates.
*/
2017-03-05 15:51:33 -08:00
SYN_ID_FULL ( info - > identity ) = = 0x801 ) ) {
error = synaptics_send_cmd ( psmouse ,
SYN_QUE_EXT_MIN_COORDS , resp ) ;
if ( error ) {
2011-10-10 18:27:03 -07:00
psmouse_warn ( psmouse ,
" device claims to have min coordinates query, but I'm not able to read it. \n " ) ;
2011-07-09 12:32:56 -07:00
} else {
2017-03-05 15:51:33 -08:00
info - > x_min = ( resp [ 0 ] < < 5 ) | ( ( resp [ 1 ] & 0x0f ) < < 1 ) ;
info - > y_min = ( resp [ 2 ] < < 5 ) | ( ( resp [ 1 ] & 0xf0 ) > > 3 ) ;
2015-03-08 22:28:29 -07:00
psmouse_info ( psmouse ,
" queried min coordinates: x [%d..], y [%d..] \n " ,
2017-03-05 15:51:33 -08:00
info - > x_min , info - > y_min ) ;
2010-05-10 23:06:52 -07:00
}
2009-06-10 23:27:24 -07:00
}
return 0 ;
}
2017-03-05 15:51:33 -08:00
static int synaptics_query_hardware ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
2005-04-16 15:20:36 -07:00
{
2017-03-05 15:51:33 -08:00
int error ;
2005-04-16 15:20:36 -07:00
2017-05-29 19:57:19 -07:00
memset ( info , 0 , sizeof ( * info ) ) ;
2017-03-05 15:51:33 -08:00
error = synaptics_identify ( psmouse , info ) ;
if ( error )
return error ;
error = synaptics_model_id ( psmouse , info ) ;
if ( error )
return error ;
error = synaptics_firmware_id ( psmouse , info ) ;
if ( error )
return error ;
error = synaptics_query_modes ( psmouse , info ) ;
if ( error )
return error ;
error = synaptics_capability ( psmouse , info ) ;
if ( error )
return error ;
error = synaptics_resolution ( psmouse , info ) ;
if ( error )
return error ;
2015-03-08 22:27:37 -07:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
2017-03-02 14:13:53 -08:00
# endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
# ifdef CONFIG_MOUSE_PS2_SYNAPTICS
static bool cr48_profile_sensor ;
# define ANY_BOARD_ID 0
struct min_max_quirk {
const char * const * pnp_ids ;
struct {
u32 min , max ;
} board_id ;
u32 x_min , x_max , y_min , y_max ;
} ;
static const struct min_max_quirk min_max_pnpid_table [ ] = {
{
( const char * const [ ] ) { " LEN0033 " , NULL } ,
{ ANY_BOARD_ID , ANY_BOARD_ID } ,
1024 , 5052 , 2258 , 4832
} ,
{
( const char * const [ ] ) { " LEN0042 " , NULL } ,
{ ANY_BOARD_ID , ANY_BOARD_ID } ,
1232 , 5710 , 1156 , 4696
} ,
{
( const char * const [ ] ) { " LEN0034 " , " LEN0036 " , " LEN0037 " ,
" LEN0039 " , " LEN2002 " , " LEN2004 " ,
NULL } ,
{ ANY_BOARD_ID , 2961 } ,
1024 , 5112 , 2024 , 4832
} ,
{
( const char * const [ ] ) { " LEN2000 " , NULL } ,
{ ANY_BOARD_ID , ANY_BOARD_ID } ,
1024 , 5113 , 2021 , 4832
} ,
{
( const char * const [ ] ) { " LEN2001 " , NULL } ,
{ ANY_BOARD_ID , ANY_BOARD_ID } ,
1024 , 5022 , 2508 , 4832
} ,
{
( const char * const [ ] ) { " LEN2006 " , NULL } ,
{ 2691 , 2691 } ,
1024 , 5045 , 2457 , 4832
} ,
{
( const char * const [ ] ) { " LEN2006 " , NULL } ,
{ ANY_BOARD_ID , ANY_BOARD_ID } ,
1264 , 5675 , 1171 , 4688
} ,
{ }
} ;
/*****************************************************************************
* Synaptics communications functions
****************************************************************************/
/*
* Synaptics touchpads report the y coordinate from bottom to top, which is
* opposite from what userspace expects.
* This function is used to invert y before reporting.
*/
static int synaptics_invert_y ( int y )
{
return YMAX_NOMINAL + YMIN_NOMINAL - y ;
}
/*
* Apply quirk(s) if the hardware matches
*/
static void synaptics_apply_quirks ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
{
int i ;
for ( i = 0 ; min_max_pnpid_table [ i ] . pnp_ids ; i + + ) {
if ( ! psmouse_matches_pnp_id ( psmouse ,
min_max_pnpid_table [ i ] . pnp_ids ) )
continue ;
if ( min_max_pnpid_table [ i ] . board_id . min ! = ANY_BOARD_ID & &
info - > board_id < min_max_pnpid_table [ i ] . board_id . min )
continue ;
if ( min_max_pnpid_table [ i ] . board_id . max ! = ANY_BOARD_ID & &
info - > board_id > min_max_pnpid_table [ i ] . board_id . max )
continue ;
info - > x_min = min_max_pnpid_table [ i ] . x_min ;
info - > x_max = min_max_pnpid_table [ i ] . x_max ;
info - > y_min = min_max_pnpid_table [ i ] . y_min ;
info - > y_max = min_max_pnpid_table [ i ] . y_max ;
psmouse_info ( psmouse ,
" quirked min/max coordinates: x [%d..%d], y [%d..%d] \n " ,
info - > x_min , info - > x_max ,
info - > y_min , info - > y_max ) ;
break ;
}
}
2017-08-28 10:26:12 -07:00
static bool synaptics_has_agm ( struct synaptics_data * priv )
{
return ( SYN_CAP_ADV_GESTURE ( priv - > info . ext_cap_0c ) | |
SYN_CAP_IMAGE_SENSOR ( priv - > info . ext_cap_0c ) ) ;
}
2011-11-08 00:00:35 -08:00
static int synaptics_set_advanced_gesture_mode ( struct psmouse * psmouse )
{
2017-03-24 11:20:38 -07:00
static u8 param = 0xc8 ;
2017-03-23 18:38:14 -07:00
int error ;
2011-11-08 00:00:35 -08:00
2018-01-02 12:03:02 -08:00
error = ps2_sliced_command ( & psmouse - > ps2dev , SYN_QUE_MODEL ) ;
2017-03-23 18:38:14 -07:00
if ( error )
return error ;
2011-11-08 00:00:35 -08:00
2017-03-23 18:38:14 -07:00
error = ps2_command ( & psmouse - > ps2dev , & param , PSMOUSE_CMD_SETRATE ) ;
if ( error )
return error ;
2011-11-08 00:00:35 -08:00
return 0 ;
}
static int synaptics_set_mode ( struct psmouse * psmouse )
2005-04-16 15:20:36 -07:00
{
struct synaptics_data * priv = psmouse - > private ;
2017-03-23 18:38:14 -07:00
int error ;
2005-04-16 15:20:36 -07:00
2011-11-08 00:00:35 -08:00
priv - > mode = 0 ;
2015-10-02 10:31:32 -07:00
if ( priv - > absolute_mode )
2011-11-08 00:00:35 -08:00
priv - > mode | = SYN_BIT_ABSOLUTE_MODE ;
2015-10-02 10:31:32 -07:00
if ( priv - > disable_gesture )
2005-04-16 15:20:36 -07:00
priv - > mode | = SYN_BIT_DISABLE_GESTURE ;
2011-11-08 00:00:35 -08:00
if ( psmouse - > rate > = 80 )
priv - > mode | = SYN_BIT_HIGH_RATE ;
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_EXTENDED ( priv - > info . capabilities ) )
2015-10-02 10:31:32 -07:00
priv - > mode | = SYN_BIT_W_MODE ;
2005-04-16 15:20:36 -07:00
2017-03-23 18:38:14 -07:00
error = synaptics_mode_cmd ( psmouse , priv - > mode ) ;
if ( error )
return error ;
2005-04-16 15:20:36 -07:00
2017-08-28 10:26:12 -07:00
if ( priv - > absolute_mode & & synaptics_has_agm ( priv ) ) {
2017-03-23 18:38:14 -07:00
error = synaptics_set_advanced_gesture_mode ( psmouse ) ;
if ( error ) {
psmouse_err ( psmouse ,
" Advanced gesture mode init failed: %d \n " ,
error ) ;
return error ;
}
2011-11-08 00:00:35 -08:00
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
static void synaptics_set_rate ( struct psmouse * psmouse , unsigned int rate )
{
struct synaptics_data * priv = psmouse - > private ;
if ( rate > = 80 ) {
priv - > mode | = SYN_BIT_HIGH_RATE ;
psmouse - > rate = 80 ;
} else {
priv - > mode & = ~ SYN_BIT_HIGH_RATE ;
psmouse - > rate = 40 ;
}
synaptics_mode_cmd ( psmouse , priv - > mode ) ;
}
/*****************************************************************************
* Synaptics pass-through PS/2 port support
****************************************************************************/
2017-03-24 11:20:38 -07:00
static int synaptics_pt_write ( struct serio * serio , u8 c )
2005-04-16 15:20:36 -07:00
{
struct psmouse * parent = serio_get_drvdata ( serio - > parent ) ;
2017-03-23 18:38:14 -07:00
u8 rate_param = SYN_PS_CLIENT_CMD ; /* indicates that we want pass-through port */
int error ;
2018-01-02 12:03:02 -08:00
error = ps2_sliced_command ( & parent - > ps2dev , c ) ;
2017-03-23 18:38:14 -07:00
if ( error )
return error ;
error = ps2_command ( & parent - > ps2dev , & rate_param , PSMOUSE_CMD_SETRATE ) ;
if ( error )
return error ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2010-10-04 21:46:10 -07:00
static int synaptics_pt_start ( struct serio * serio )
{
struct psmouse * parent = serio_get_drvdata ( serio - > parent ) ;
struct synaptics_data * priv = parent - > private ;
serio_pause_rx ( parent - > ps2dev . serio ) ;
priv - > pt_port = serio ;
serio_continue_rx ( parent - > ps2dev . serio ) ;
return 0 ;
}
static void synaptics_pt_stop ( struct serio * serio )
{
struct psmouse * parent = serio_get_drvdata ( serio - > parent ) ;
struct synaptics_data * priv = parent - > private ;
serio_pause_rx ( parent - > ps2dev . serio ) ;
priv - > pt_port = NULL ;
serio_continue_rx ( parent - > ps2dev . serio ) ;
}
2017-03-24 11:20:38 -07:00
static int synaptics_is_pt_packet ( u8 * buf )
2005-04-16 15:20:36 -07:00
{
return ( buf [ 0 ] & 0xFC ) = = 0x84 & & ( buf [ 3 ] & 0xCC ) = = 0xC4 ;
}
2017-03-24 11:20:38 -07:00
static void synaptics_pass_pt_packet ( struct serio * ptport , u8 * packet )
2005-04-16 15:20:36 -07:00
{
struct psmouse * child = serio_get_drvdata ( ptport ) ;
if ( child & & child - > state = = PSMOUSE_ACTIVATED ) {
2017-02-09 11:34:08 -08:00
serio_interrupt ( ptport , packet [ 1 ] , 0 ) ;
2006-10-05 14:55:46 +01:00
serio_interrupt ( ptport , packet [ 4 ] , 0 ) ;
serio_interrupt ( ptport , packet [ 5 ] , 0 ) ;
2005-07-24 00:53:32 -05:00
if ( child - > pktsize = = 4 )
2006-10-05 14:55:46 +01:00
serio_interrupt ( ptport , packet [ 2 ] , 0 ) ;
2015-03-08 22:35:41 -07:00
} else {
2006-10-05 14:55:46 +01:00
serio_interrupt ( ptport , packet [ 1 ] , 0 ) ;
2015-03-08 22:35:41 -07:00
}
2005-04-16 15:20:36 -07:00
}
static void synaptics_pt_activate ( struct psmouse * psmouse )
{
struct synaptics_data * priv = psmouse - > private ;
2010-10-04 21:46:10 -07:00
struct psmouse * child = serio_get_drvdata ( priv - > pt_port ) ;
2005-04-16 15:20:36 -07:00
/* adjust the touchpad to child's choice of protocol */
if ( child ) {
2005-07-24 00:53:32 -05:00
if ( child - > pktsize = = 4 )
2005-04-16 15:20:36 -07:00
priv - > mode | = SYN_BIT_FOUR_BYTE_CLIENT ;
else
priv - > mode & = ~ SYN_BIT_FOUR_BYTE_CLIENT ;
if ( synaptics_mode_cmd ( psmouse , priv - > mode ) )
2011-10-10 18:27:03 -07:00
psmouse_warn ( psmouse ,
" failed to switch guest protocol \n " ) ;
2005-04-16 15:20:36 -07:00
}
}
static void synaptics_pt_create ( struct psmouse * psmouse )
{
struct serio * serio ;
2006-03-14 00:09:16 -05:00
serio = kzalloc ( sizeof ( struct serio ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! serio ) {
2011-10-10 18:27:03 -07:00
psmouse_err ( psmouse ,
" not enough memory for pass-through port \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
serio - > id . type = SERIO_PS_PSTHRU ;
2022-08-18 15:05:06 -07:00
strscpy ( serio - > name , " Synaptics pass-through " , sizeof ( serio - > name ) ) ;
strscpy ( serio - > phys , " synaptics-pt/serio0 " , sizeof ( serio - > phys ) ) ;
2005-04-16 15:20:36 -07:00
serio - > write = synaptics_pt_write ;
2010-10-04 21:46:10 -07:00
serio - > start = synaptics_pt_start ;
serio - > stop = synaptics_pt_stop ;
2005-04-16 15:20:36 -07:00
serio - > parent = psmouse - > ps2dev . serio ;
psmouse - > pt_activate = synaptics_pt_activate ;
2011-10-10 18:27:03 -07:00
psmouse_info ( psmouse , " serio: %s port at %s \n " ,
serio - > name , psmouse - > phys ) ;
2005-04-16 15:20:36 -07:00
serio_register_port ( serio ) ;
}
/*****************************************************************************
* Functions to interpret the absolute mode packets
****************************************************************************/
2017-03-24 11:20:38 -07:00
static void synaptics_parse_agm ( const u8 buf [ ] ,
2011-08-23 23:02:31 -07:00
struct synaptics_data * priv ,
struct synaptics_hw_state * hw )
2011-08-23 23:00:33 -07:00
{
struct synaptics_hw_state * agm = & priv - > agm ;
2011-08-23 23:02:31 -07:00
int agm_packet_type ;
2011-08-23 23:00:33 -07:00
2011-08-23 23:02:31 -07:00
agm_packet_type = ( buf [ 5 ] & 0x30 ) > > 4 ;
switch ( agm_packet_type ) {
case 1 :
/* Gesture packet: (x, y, z) half resolution */
agm - > w = hw - > w ;
agm - > x = ( ( ( buf [ 4 ] & 0x0f ) < < 8 ) | buf [ 1 ] ) < < 1 ;
agm - > y = ( ( ( buf [ 4 ] & 0xf0 ) < < 4 ) | buf [ 2 ] ) < < 1 ;
agm - > z = ( ( buf [ 3 ] & 0x30 ) | ( buf [ 5 ] & 0x0f ) ) < < 1 ;
break ;
case 2 :
2014-12-29 14:15:24 -08:00
/* AGM-CONTACT packet: we are only interested in the count */
priv - > agm_count = buf [ 1 ] ;
2011-08-23 23:02:31 -07:00
break ;
default :
break ;
}
2011-08-23 23:00:33 -07:00
}
2017-03-24 11:20:38 -07:00
static void synaptics_parse_ext_buttons ( const u8 buf [ ] ,
2015-03-08 22:30:43 -07:00
struct synaptics_data * priv ,
struct synaptics_hw_state * hw )
{
unsigned int ext_bits =
2017-03-05 15:51:33 -08:00
( SYN_CAP_MULTI_BUTTON_NO ( priv - > info . ext_cap ) + 1 ) > > 1 ;
2015-03-08 22:30:43 -07:00
unsigned int ext_mask = GENMASK ( ext_bits - 1 , 0 ) ;
hw - > ext_buttons = buf [ 4 ] & ext_mask ;
hw - > ext_buttons | = ( buf [ 5 ] & ext_mask ) < < ext_bits ;
}
2017-03-24 11:20:38 -07:00
static int synaptics_parse_hw_state ( const u8 buf [ ] ,
2010-12-21 18:11:25 +01:00
struct synaptics_data * priv ,
struct synaptics_hw_state * hw )
2005-04-16 15:20:36 -07:00
{
memset ( hw , 0 , sizeof ( struct synaptics_hw_state ) ) ;
2017-03-05 15:51:33 -08:00
if ( SYN_MODEL_NEWABS ( priv - > info . model_id ) ) {
2005-04-16 15:20:36 -07:00
hw - > w = ( ( ( buf [ 0 ] & 0x30 ) > > 2 ) |
( ( buf [ 0 ] & 0x04 ) > > 1 ) |
( ( buf [ 3 ] & 0x04 ) > > 2 ) ) ;
2017-08-28 10:26:12 -07:00
if ( synaptics_has_agm ( priv ) & & hw - > w = = 2 ) {
2014-08-30 13:51:06 -07:00
synaptics_parse_agm ( buf , priv , hw ) ;
return 1 ;
}
hw - > x = ( ( ( buf [ 3 ] & 0x10 ) < < 8 ) |
( ( buf [ 1 ] & 0x0f ) < < 8 ) |
buf [ 4 ] ) ;
hw - > y = ( ( ( buf [ 3 ] & 0x20 ) < < 7 ) |
( ( buf [ 1 ] & 0xf0 ) < < 4 ) |
buf [ 5 ] ) ;
hw - > z = buf [ 2 ] ;
2005-04-16 15:20:36 -07:00
hw - > left = ( buf [ 0 ] & 0x01 ) ? 1 : 0 ;
hw - > right = ( buf [ 0 ] & 0x02 ) ? 1 : 0 ;
2014-12-29 14:43:44 -08:00
if ( priv - > is_forcepad ) {
2014-08-30 13:51:06 -07:00
/*
* ForcePads, like Clickpads, use middle button
* bits to report primary button clicks.
* Unfortunately they report primary button not
* only when user presses on the pad above certain
* threshold, but also when there are more than one
* finger on the touchpad, which interferes with
* out multi-finger gestures.
*/
if ( hw - > z = = 0 ) {
/* No contacts */
priv - > press = priv - > report_press = false ;
} else if ( hw - > w > = 4 & & ( ( buf [ 0 ] ^ buf [ 3 ] ) & 0x01 ) ) {
/*
* Single-finger touch with pressure above
* the threshold. If pressure stays long
* enough, we'll start reporting primary
* button. We rely on the device continuing
* sending data even if finger does not
* move.
*/
if ( ! priv - > press ) {
priv - > press_start = jiffies ;
priv - > press = true ;
} else if ( time_after ( jiffies ,
priv - > press_start +
msecs_to_jiffies ( 50 ) ) ) {
priv - > report_press = true ;
}
} else {
priv - > press = false ;
}
hw - > left = priv - > report_press ;
2017-03-05 15:51:33 -08:00
} else if ( SYN_CAP_CLICKPAD ( priv - > info . ext_cap_0c ) ) {
2010-04-19 10:37:21 -07:00
/*
* Clickpad's button is transmitted as middle button,
* however, since it is primary button, we will report
* it as BTN_LEFT.
*/
hw - > left = ( ( buf [ 0 ] ^ buf [ 3 ] ) & 0x01 ) ? 1 : 0 ;
2017-03-05 15:51:33 -08:00
} else if ( SYN_CAP_MIDDLE_BUTTON ( priv - > info . capabilities ) ) {
2005-04-16 15:20:36 -07:00
hw - > middle = ( ( buf [ 0 ] ^ buf [ 3 ] ) & 0x01 ) ? 1 : 0 ;
if ( hw - > w = = 2 )
2017-03-24 11:20:38 -07:00
hw - > scroll = ( s8 ) buf [ 1 ] ;
2005-04-16 15:20:36 -07:00
}
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_FOUR_BUTTON ( priv - > info . capabilities ) ) {
2005-04-16 15:20:36 -07:00
hw - > up = ( ( buf [ 0 ] ^ buf [ 3 ] ) & 0x01 ) ? 1 : 0 ;
hw - > down = ( ( buf [ 0 ] ^ buf [ 3 ] ) & 0x02 ) ? 1 : 0 ;
}
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_MULTI_BUTTON_NO ( priv - > info . ext_cap ) > 0 & &
2005-04-16 15:20:36 -07:00
( ( buf [ 0 ] ^ buf [ 3 ] ) & 0x02 ) ) {
2015-03-08 22:30:43 -07:00
synaptics_parse_ext_buttons ( buf , priv , hw ) ;
2005-04-16 15:20:36 -07:00
}
} else {
hw - > x = ( ( ( buf [ 1 ] & 0x1f ) < < 8 ) | buf [ 2 ] ) ;
hw - > y = ( ( ( buf [ 4 ] & 0x1f ) < < 8 ) | buf [ 5 ] ) ;
hw - > z = ( ( ( buf [ 0 ] & 0x30 ) < < 2 ) | ( buf [ 3 ] & 0x3F ) ) ;
hw - > w = ( ( ( buf [ 1 ] & 0x80 ) > > 4 ) | ( ( buf [ 0 ] & 0x04 ) > > 1 ) ) ;
hw - > left = ( buf [ 0 ] & 0x01 ) ? 1 : 0 ;
hw - > right = ( buf [ 0 ] & 0x02 ) ? 1 : 0 ;
}
2010-12-21 18:11:25 +01:00
2012-09-28 10:29:21 -07:00
/*
* Convert wrap-around values to negative. (X|Y)_MAX_POSITIVE
* is used by some firmware to indicate a finger at the edge of
* the touchpad whose precise position cannot be determined, so
* convert these values to the maximum axis value.
*/
2012-07-24 23:54:11 -07:00
if ( hw - > x > X_MAX_POSITIVE )
hw - > x - = 1 < < ABS_POS_BITS ;
2012-09-28 10:29:21 -07:00
else if ( hw - > x = = X_MAX_POSITIVE )
hw - > x = XMAX ;
2012-07-24 23:54:11 -07:00
if ( hw - > y > Y_MAX_POSITIVE )
hw - > y - = 1 < < ABS_POS_BITS ;
2012-09-28 10:29:21 -07:00
else if ( hw - > y = = Y_MAX_POSITIVE )
hw - > y = YMAX ;
2012-07-24 23:54:11 -07:00
2010-12-21 18:11:25 +01:00
return 0 ;
}
2011-07-06 22:42:52 -07:00
static void synaptics_report_semi_mt_slot ( struct input_dev * dev , int slot ,
bool active , int x , int y )
2010-12-21 18:11:25 +01:00
{
input_mt_slot ( dev , slot ) ;
input_mt_report_slot_state ( dev , MT_TOOL_FINGER , active ) ;
if ( active ) {
input_report_abs ( dev , ABS_MT_POSITION_X , x ) ;
2011-08-23 23:00:24 -07:00
input_report_abs ( dev , ABS_MT_POSITION_Y , synaptics_invert_y ( y ) ) ;
2010-12-21 18:11:25 +01:00
}
}
static void synaptics_report_semi_mt_data ( struct input_dev * dev ,
const struct synaptics_hw_state * a ,
const struct synaptics_hw_state * b ,
int num_fingers )
{
if ( num_fingers > = 2 ) {
2011-07-06 22:42:52 -07:00
synaptics_report_semi_mt_slot ( dev , 0 , true , min ( a - > x , b - > x ) ,
min ( a - > y , b - > y ) ) ;
synaptics_report_semi_mt_slot ( dev , 1 , true , max ( a - > x , b - > x ) ,
max ( a - > y , b - > y ) ) ;
2010-12-21 18:11:25 +01:00
} else if ( num_fingers = = 1 ) {
2011-07-06 22:42:52 -07:00
synaptics_report_semi_mt_slot ( dev , 0 , true , a - > x , a - > y ) ;
synaptics_report_semi_mt_slot ( dev , 1 , false , 0 , 0 ) ;
2010-12-21 18:11:25 +01:00
} else {
2011-07-06 22:42:52 -07:00
synaptics_report_semi_mt_slot ( dev , 0 , false , 0 , 0 ) ;
synaptics_report_semi_mt_slot ( dev , 1 , false , 0 , 0 ) ;
2010-12-21 18:11:25 +01:00
}
2005-04-16 15:20:36 -07:00
}
2015-03-08 22:32:43 -07:00
static void synaptics_report_ext_buttons ( struct psmouse * psmouse ,
const struct synaptics_hw_state * hw )
2011-08-23 23:02:25 -07:00
{
struct input_dev * dev = psmouse - > dev ;
struct synaptics_data * priv = psmouse - > private ;
2017-03-05 15:51:33 -08:00
int ext_bits = ( SYN_CAP_MULTI_BUTTON_NO ( priv - > info . ext_cap ) + 1 ) > > 1 ;
2011-08-23 23:02:25 -07:00
int i ;
2017-03-05 15:51:33 -08:00
if ( ! SYN_CAP_MULTI_BUTTON_NO ( priv - > info . ext_cap ) )
2015-03-08 22:32:43 -07:00
return ;
2016-03-17 17:12:54 -07:00
/* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
2017-03-05 15:51:33 -08:00
if ( ( SYN_ID_FULL ( priv - > info . identity ) = = 0x801 | |
SYN_ID_FULL ( priv - > info . identity ) = = 0x802 ) & &
2015-03-08 22:32:43 -07:00
! ( ( psmouse - > packet [ 0 ] ^ psmouse - > packet [ 3 ] ) & 0x02 ) )
return ;
2017-03-05 15:51:33 -08:00
if ( ! SYN_CAP_EXT_BUTTONS_STICK ( priv - > info . ext_cap_10 ) ) {
2015-03-08 22:35:41 -07:00
for ( i = 0 ; i < ext_bits ; i + + ) {
input_report_key ( dev , BTN_0 + 2 * i ,
2017-03-23 14:56:06 -07:00
hw - > ext_buttons & BIT ( i ) ) ;
2015-03-08 22:35:41 -07:00
input_report_key ( dev , BTN_1 + 2 * i ,
2017-03-23 14:56:06 -07:00
hw - > ext_buttons & BIT ( i + ext_bits ) ) ;
2015-03-08 22:35:41 -07:00
}
return ;
2015-03-08 22:32:43 -07:00
}
2015-03-08 22:35:41 -07:00
/*
* This generation of touchpads has the trackstick buttons
* physically wired to the touchpad. Re-route them through
* the pass-through interface.
*/
2017-02-09 11:34:08 -08:00
if ( priv - > pt_port ) {
u8 pt_buttons ;
2015-03-08 22:35:41 -07:00
2017-02-09 11:34:08 -08:00
/* The trackstick expects at most 3 buttons */
2017-03-23 10:02:50 -07:00
pt_buttons = SYN_EXT_BUTTON_STICK_L ( hw - > ext_buttons ) |
SYN_EXT_BUTTON_STICK_R ( hw - > ext_buttons ) < < 1 |
SYN_EXT_BUTTON_STICK_M ( hw - > ext_buttons ) < < 2 ;
2015-03-08 22:35:41 -07:00
2017-02-09 11:34:08 -08:00
serio_interrupt ( priv - > pt_port ,
PSMOUSE_OOB_EXTRA_BTNS , SERIO_OOB_DATA ) ;
serio_interrupt ( priv - > pt_port , pt_buttons , SERIO_OOB_DATA ) ;
}
2015-03-08 22:32:43 -07:00
}
2011-08-23 23:02:25 -07:00
static void synaptics_report_buttons ( struct psmouse * psmouse ,
const struct synaptics_hw_state * hw )
{
struct input_dev * dev = psmouse - > dev ;
struct synaptics_data * priv = psmouse - > private ;
input_report_key ( dev , BTN_LEFT , hw - > left ) ;
input_report_key ( dev , BTN_RIGHT , hw - > right ) ;
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_MIDDLE_BUTTON ( priv - > info . capabilities ) )
2011-08-23 23:02:25 -07:00
input_report_key ( dev , BTN_MIDDLE , hw - > middle ) ;
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_FOUR_BUTTON ( priv - > info . capabilities ) ) {
2011-08-23 23:02:25 -07:00
input_report_key ( dev , BTN_FORWARD , hw - > up ) ;
input_report_key ( dev , BTN_BACK , hw - > down ) ;
}
2015-03-08 22:32:43 -07:00
synaptics_report_ext_buttons ( psmouse , hw ) ;
2011-08-23 23:02:25 -07:00
}
static void synaptics_report_mt_data ( struct psmouse * psmouse ,
2014-12-29 14:15:24 -08:00
const struct synaptics_hw_state * sgm ,
int num_fingers )
2011-08-23 23:02:25 -07:00
{
struct input_dev * dev = psmouse - > dev ;
struct synaptics_data * priv = psmouse - > private ;
2014-12-29 14:15:24 -08:00
const struct synaptics_hw_state * hw [ 2 ] = { sgm , & priv - > agm } ;
struct input_mt_pos pos [ 2 ] ;
int slot [ 2 ] , nsemi , i ;
2011-08-23 23:02:25 -07:00
2014-12-29 14:15:24 -08:00
nsemi = clamp_val ( num_fingers , 0 , 2 ) ;
2011-08-23 23:02:40 -07:00
2014-12-29 14:15:24 -08:00
for ( i = 0 ; i < nsemi ; i + + ) {
pos [ i ] . x = hw [ i ] - > x ;
pos [ i ] . y = synaptics_invert_y ( hw [ i ] - > y ) ;
2011-08-23 23:02:25 -07:00
}
2017-03-05 15:51:33 -08:00
input_mt_assign_slots ( dev , slot , pos , nsemi , DMAX * priv - > info . x_res ) ;
2014-12-29 14:15:24 -08:00
for ( i = 0 ; i < nsemi ; i + + ) {
input_mt_slot ( dev , slot [ i ] ) ;
input_mt_report_slot_state ( dev , MT_TOOL_FINGER , true ) ;
input_report_abs ( dev , ABS_MT_POSITION_X , pos [ i ] . x ) ;
input_report_abs ( dev , ABS_MT_POSITION_Y , pos [ i ] . y ) ;
input_report_abs ( dev , ABS_MT_PRESSURE , hw [ i ] - > z ) ;
}
input_mt_drop_unused ( dev ) ;
2011-08-23 23:02:25 -07:00
/* Don't use active slot count to generate BTN_TOOL events. */
input_mt_report_pointer_emulation ( dev , false ) ;
/* Send the number of fingers reported by touchpad itself. */
2014-12-29 14:15:24 -08:00
input_mt_report_finger_count ( dev , num_fingers ) ;
2011-08-23 23:02:25 -07:00
synaptics_report_buttons ( psmouse , sgm ) ;
input_sync ( dev ) ;
}
static void synaptics_image_sensor_process ( struct psmouse * psmouse ,
struct synaptics_hw_state * sgm )
{
2011-08-23 23:02:40 -07:00
struct synaptics_data * priv = psmouse - > private ;
2014-12-29 14:15:24 -08:00
int num_fingers ;
2011-08-23 23:02:40 -07:00
/*
* Update mt_state using the new finger count and current mt_state.
*/
2011-08-23 23:02:25 -07:00
if ( sgm - > z = = 0 )
2014-12-29 14:15:24 -08:00
num_fingers = 0 ;
2011-08-23 23:02:25 -07:00
else if ( sgm - > w > = 4 )
2014-12-29 14:15:24 -08:00
num_fingers = 1 ;
2011-08-23 23:02:25 -07:00
else if ( sgm - > w = = 0 )
2014-12-29 14:15:24 -08:00
num_fingers = 2 ;
else if ( sgm - > w = = 1 )
num_fingers = priv - > agm_count ? priv - > agm_count : 3 ;
2011-08-23 23:02:56 -07:00
else
2014-12-29 14:15:24 -08:00
num_fingers = 4 ;
2011-08-23 23:02:25 -07:00
/* Send resulting input events to user space */
2014-12-29 14:15:24 -08:00
synaptics_report_mt_data ( psmouse , sgm , num_fingers ) ;
2011-08-23 23:02:25 -07:00
}
2017-08-28 10:26:12 -07:00
static bool synaptics_has_multifinger ( struct synaptics_data * priv )
{
if ( SYN_CAP_MULTIFINGER ( priv - > info . capabilities ) )
return true ;
/* Advanced gesture mode also sends multi finger data */
return synaptics_has_agm ( priv ) ;
}
2005-04-16 15:20:36 -07:00
/*
* called for each full received packet from the touchpad
*/
static void synaptics_process_packet ( struct psmouse * psmouse )
{
2005-09-15 02:01:44 -05:00
struct input_dev * dev = psmouse - > dev ;
2005-04-16 15:20:36 -07:00
struct synaptics_data * priv = psmouse - > private ;
2017-03-05 15:51:33 -08:00
struct synaptics_device_info * info = & priv - > info ;
2005-04-16 15:20:36 -07:00
struct synaptics_hw_state hw ;
int num_fingers ;
int finger_width ;
2010-12-21 18:11:25 +01:00
if ( synaptics_parse_hw_state ( psmouse - > packet , priv , & hw ) )
return ;
2005-04-16 15:20:36 -07:00
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_IMAGE_SENSOR ( info - > ext_cap_0c ) ) {
2011-08-23 23:02:25 -07:00
synaptics_image_sensor_process ( psmouse , & hw ) ;
return ;
}
2005-04-16 15:20:36 -07:00
if ( hw . scroll ) {
priv - > scroll + = hw . scroll ;
while ( priv - > scroll > = 4 ) {
input_report_key ( dev , BTN_BACK , ! hw . down ) ;
input_sync ( dev ) ;
input_report_key ( dev , BTN_BACK , hw . down ) ;
input_sync ( dev ) ;
priv - > scroll - = 4 ;
}
while ( priv - > scroll < = - 4 ) {
input_report_key ( dev , BTN_FORWARD , ! hw . up ) ;
input_sync ( dev ) ;
input_report_key ( dev , BTN_FORWARD , hw . up ) ;
input_sync ( dev ) ;
priv - > scroll + = 4 ;
}
return ;
}
2010-12-18 15:42:30 +01:00
if ( hw . z > 0 & & hw . x > 1 ) {
2005-04-16 15:20:36 -07:00
num_fingers = 1 ;
finger_width = 5 ;
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_EXTENDED ( info - > capabilities ) ) {
2005-04-16 15:20:36 -07:00
switch ( hw . w ) {
case 0 . . . 1 :
2017-08-28 10:26:12 -07:00
if ( synaptics_has_multifinger ( priv ) )
2005-04-16 15:20:36 -07:00
num_fingers = hw . w + 2 ;
break ;
case 2 :
2021-01-19 18:43:11 -08:00
/*
* SYN_MODEL_PEN(info->model_id): even if
* the device supports pen, we treat it as
* a single finger.
*/
2005-04-16 15:20:36 -07:00
break ;
case 4 . . . 15 :
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_PALMDETECT ( info - > capabilities ) )
2005-04-16 15:20:36 -07:00
finger_width = hw . w ;
break ;
}
}
} else {
num_fingers = 0 ;
finger_width = 0 ;
}
2014-07-14 10:26:56 -07:00
if ( cr48_profile_sensor ) {
2014-12-29 14:17:44 -08:00
synaptics_report_mt_data ( psmouse , & hw , num_fingers ) ;
2014-07-14 10:26:56 -07:00
return ;
}
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_ADV_GESTURE ( info - > ext_cap_0c ) )
2011-08-23 23:00:33 -07:00
synaptics_report_semi_mt_data ( dev , & hw , & priv - > agm ,
num_fingers ) ;
2010-12-21 18:11:25 +01:00
2005-04-16 15:20:36 -07:00
/* Post events
* BTN_TOUCH has to be first as mousedev relies on it when doing
* absolute -> relative conversion
*/
if ( hw . z > 30 ) input_report_key ( dev , BTN_TOUCH , 1 ) ;
if ( hw . z < 25 ) input_report_key ( dev , BTN_TOUCH , 0 ) ;
2010-12-18 15:42:30 +01:00
if ( num_fingers > 0 ) {
2005-04-16 15:20:36 -07:00
input_report_abs ( dev , ABS_X , hw . x ) ;
2011-08-23 23:00:24 -07:00
input_report_abs ( dev , ABS_Y , synaptics_invert_y ( hw . y ) ) ;
2005-04-16 15:20:36 -07:00
}
input_report_abs ( dev , ABS_PRESSURE , hw . z ) ;
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_PALMDETECT ( info - > capabilities ) )
2010-07-19 09:06:15 -07:00
input_report_abs ( dev , ABS_TOOL_WIDTH , finger_width ) ;
2005-04-16 15:20:36 -07:00
input_report_key ( dev , BTN_TOOL_FINGER , num_fingers = = 1 ) ;
2017-08-28 10:26:12 -07:00
if ( synaptics_has_multifinger ( priv ) ) {
2008-11-20 15:24:42 -05:00
input_report_key ( dev , BTN_TOOL_DOUBLETAP , num_fingers = = 2 ) ;
input_report_key ( dev , BTN_TOOL_TRIPLETAP , num_fingers = = 3 ) ;
}
2011-08-23 23:02:25 -07:00
synaptics_report_buttons ( psmouse , & hw ) ;
2005-04-16 15:20:36 -07:00
input_sync ( dev ) ;
}
2017-03-24 11:20:38 -07:00
static bool synaptics_validate_byte ( struct psmouse * psmouse ,
int idx , enum synaptics_pkt_type pkt_type )
2005-04-16 15:20:36 -07:00
{
2017-03-24 11:20:38 -07:00
static const u8 newabs_mask [ ] = { 0xC8 , 0x00 , 0x00 , 0xC8 , 0x00 } ;
static const u8 newabs_rel_mask [ ] = { 0xC0 , 0x00 , 0x00 , 0xC0 , 0x00 } ;
static const u8 newabs_rslt [ ] = { 0x80 , 0x00 , 0x00 , 0xC0 , 0x00 } ;
static const u8 oldabs_mask [ ] = { 0xC0 , 0x60 , 0x00 , 0xC0 , 0x60 } ;
static const u8 oldabs_rslt [ ] = { 0xC0 , 0x00 , 0x00 , 0x80 , 0x00 } ;
const u8 * packet = psmouse - > packet ;
2005-04-16 15:20:36 -07:00
if ( idx < 0 | | idx > 4 )
2017-03-24 11:20:38 -07:00
return false ;
2005-04-16 15:20:36 -07:00
switch ( pkt_type ) {
2010-05-19 10:39:17 -07:00
case SYN_NEWABS :
case SYN_NEWABS_RELAXED :
return ( packet [ idx ] & newabs_rel_mask [ idx ] ) = = newabs_rslt [ idx ] ;
2005-04-16 15:20:36 -07:00
2010-05-19 10:39:17 -07:00
case SYN_NEWABS_STRICT :
return ( packet [ idx ] & newabs_mask [ idx ] ) = = newabs_rslt [ idx ] ;
2005-04-16 15:20:36 -07:00
2010-05-19 10:39:17 -07:00
case SYN_OLDABS :
return ( packet [ idx ] & oldabs_mask [ idx ] ) = = oldabs_rslt [ idx ] ;
default :
2011-10-10 18:27:03 -07:00
psmouse_err ( psmouse , " unknown packet type %d \n " , pkt_type ) ;
2017-03-24 11:20:38 -07:00
return false ;
2005-04-16 15:20:36 -07:00
}
}
2017-03-24 11:20:38 -07:00
static enum synaptics_pkt_type
synaptics_detect_pkt_type ( struct psmouse * psmouse )
2005-04-16 15:20:36 -07:00
{
int i ;
2017-03-24 11:20:38 -07:00
for ( i = 0 ; i < 5 ; i + + ) {
2011-10-10 18:27:03 -07:00
if ( ! synaptics_validate_byte ( psmouse , i , SYN_NEWABS_STRICT ) ) {
psmouse_info ( psmouse , " using relaxed packet validation \n " ) ;
2005-04-16 15:20:36 -07:00
return SYN_NEWABS_RELAXED ;
}
2017-03-24 11:20:38 -07:00
}
2005-04-16 15:20:36 -07:00
return SYN_NEWABS_STRICT ;
}
2006-10-05 14:55:46 +01:00
static psmouse_ret_t synaptics_process_byte ( struct psmouse * psmouse )
2005-04-16 15:20:36 -07:00
{
struct synaptics_data * priv = psmouse - > private ;
if ( psmouse - > pktcnt > = 6 ) { /* Full packet received */
if ( unlikely ( priv - > pkt_type = = SYN_NEWABS ) )
priv - > pkt_type = synaptics_detect_pkt_type ( psmouse ) ;
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_PASS_THROUGH ( priv - > info . capabilities ) & &
2010-10-04 21:46:10 -07:00
synaptics_is_pt_packet ( psmouse - > packet ) ) {
if ( priv - > pt_port )
2017-02-09 11:34:08 -08:00
synaptics_pass_pt_packet ( priv - > pt_port ,
2015-03-08 22:35:41 -07:00
psmouse - > packet ) ;
2005-04-16 15:20:36 -07:00
} else
synaptics_process_packet ( psmouse ) ;
return PSMOUSE_FULL_PACKET ;
}
2011-10-10 18:27:03 -07:00
return synaptics_validate_byte ( psmouse , psmouse - > pktcnt - 1 , priv - > pkt_type ) ?
2005-04-16 15:20:36 -07:00
PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA ;
}
/*****************************************************************************
* Driver initialization/cleanup functions
****************************************************************************/
2011-08-23 23:00:41 -07:00
static void set_abs_position_params ( struct input_dev * dev ,
2017-03-05 15:51:33 -08:00
struct synaptics_device_info * info ,
int x_code , int y_code )
2011-08-23 23:00:41 -07:00
{
2017-03-05 15:51:33 -08:00
int x_min = info - > x_min ? : XMIN_NOMINAL ;
int x_max = info - > x_max ? : XMAX_NOMINAL ;
int y_min = info - > y_min ? : YMIN_NOMINAL ;
int y_max = info - > y_max ? : YMAX_NOMINAL ;
int fuzz = SYN_CAP_REDUCED_FILTERING ( info - > ext_cap_0c ) ?
2011-08-23 23:00:41 -07:00
SYN_REDUCED_FILTER_FUZZ : 0 ;
input_set_abs_params ( dev , x_code , x_min , x_max , fuzz , 0 ) ;
input_set_abs_params ( dev , y_code , y_min , y_max , fuzz , 0 ) ;
2017-03-05 15:51:33 -08:00
input_abs_set_res ( dev , x_code , info - > x_res ) ;
input_abs_set_res ( dev , y_code , info - > y_res ) ;
2011-08-23 23:00:41 -07:00
}
2017-04-03 16:54:04 -07:00
static int set_input_params ( struct psmouse * psmouse ,
struct synaptics_data * priv )
2005-04-16 15:20:36 -07:00
{
2014-04-19 22:26:41 -07:00
struct input_dev * dev = psmouse - > dev ;
2017-03-05 15:51:33 -08:00
struct synaptics_device_info * info = & priv - > info ;
2005-04-16 15:20:36 -07:00
int i ;
2017-04-03 16:54:04 -07:00
int error ;
2005-04-16 15:20:36 -07:00
2017-04-03 16:37:24 -07:00
/* Reset default psmouse capabilities */
__clear_bit ( EV_REL , dev - > evbit ) ;
bitmap_zero ( dev - > relbit , REL_CNT ) ;
bitmap_zero ( dev - > keybit , KEY_CNT ) ;
2011-11-08 00:00:35 -08:00
/* Things that apply to both modes */
2010-12-16 09:52:23 +01:00
__set_bit ( INPUT_PROP_POINTER , dev - > propbit ) ;
2017-04-03 16:37:24 -07:00
input_set_capability ( dev , EV_KEY , BTN_LEFT ) ;
/* Clickpads report only left button */
if ( ! SYN_CAP_CLICKPAD ( info - > ext_cap_0c ) ) {
input_set_capability ( dev , EV_KEY , BTN_RIGHT ) ;
if ( SYN_CAP_MIDDLE_BUTTON ( info - > capabilities ) )
input_set_capability ( dev , EV_KEY , BTN_MIDDLE ) ;
}
2011-11-08 00:00:35 -08:00
if ( ! priv - > absolute_mode ) {
/* Relative mode */
2017-04-03 16:37:24 -07:00
input_set_capability ( dev , EV_REL , REL_X ) ;
input_set_capability ( dev , EV_REL , REL_Y ) ;
2017-04-03 16:54:04 -07:00
return 0 ;
2011-11-08 00:00:35 -08:00
}
/* Absolute mode */
2017-03-05 15:51:33 -08:00
set_abs_position_params ( dev , & priv - > info , ABS_X , ABS_Y ) ;
2005-04-16 15:20:36 -07:00
input_set_abs_params ( dev , ABS_PRESSURE , 0 , 255 , 0 , 0 ) ;
2010-07-19 09:06:15 -07:00
2014-07-14 10:26:56 -07:00
if ( cr48_profile_sensor )
input_set_abs_params ( dev , ABS_MT_PRESSURE , 0 , 255 , 0 , 0 ) ;
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_IMAGE_SENSOR ( info - > ext_cap_0c ) ) {
set_abs_position_params ( dev , info ,
ABS_MT_POSITION_X , ABS_MT_POSITION_Y ) ;
2011-08-23 23:02:25 -07:00
/* Image sensors can report per-contact pressure */
input_set_abs_params ( dev , ABS_MT_PRESSURE , 0 , 255 , 0 , 0 ) ;
2017-04-03 16:54:04 -07:00
error = input_mt_init_slots ( dev , 2 ,
INPUT_MT_POINTER | INPUT_MT_TRACK ) ;
if ( error )
return error ;
2011-08-23 23:02:56 -07:00
/* Image sensors can signal 4 and 5 finger clicks */
2017-04-03 16:37:24 -07:00
input_set_capability ( dev , EV_KEY , BTN_TOOL_QUADTAP ) ;
input_set_capability ( dev , EV_KEY , BTN_TOOL_QUINTTAP ) ;
2017-03-05 15:51:33 -08:00
} else if ( SYN_CAP_ADV_GESTURE ( info - > ext_cap_0c ) ) {
set_abs_position_params ( dev , info ,
ABS_MT_POSITION_X , ABS_MT_POSITION_Y ) ;
2014-07-14 10:26:56 -07:00
/*
* Profile sensor in CR-48 tracks contacts reasonably well,
* other non-image sensors with AGM use semi-mt.
*/
2017-04-03 16:54:04 -07:00
error = input_mt_init_slots ( dev , 2 ,
INPUT_MT_POINTER |
( cr48_profile_sensor ?
INPUT_MT_TRACK :
INPUT_MT_SEMI_MT ) ) ;
if ( error )
return error ;
2018-01-16 15:20:58 -08:00
/*
* For semi-mt devices we send ABS_X/Y ourselves instead of
* input_mt_report_pointer_emulation. But
* input_mt_init_slots() resets the fuzz to 0, leading to a
* filtered ABS_MT_POSITION_X but an unfiltered ABS_X
* position. Let's re-initialize ABS_X/Y here.
*/
if ( ! cr48_profile_sensor )
set_abs_position_params ( dev , & priv - > info , ABS_X , ABS_Y ) ;
2010-12-21 18:11:25 +01:00
}
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_PALMDETECT ( info - > capabilities ) )
2010-07-19 09:06:15 -07:00
input_set_abs_params ( dev , ABS_TOOL_WIDTH , 0 , 15 , 0 , 0 ) ;
2005-04-16 15:20:36 -07:00
2017-04-03 16:37:24 -07:00
input_set_capability ( dev , EV_KEY , BTN_TOUCH ) ;
input_set_capability ( dev , EV_KEY , BTN_TOOL_FINGER ) ;
2005-04-16 15:20:36 -07:00
2017-08-28 10:26:12 -07:00
if ( synaptics_has_multifinger ( priv ) ) {
2017-04-03 16:37:24 -07:00
input_set_capability ( dev , EV_KEY , BTN_TOOL_DOUBLETAP ) ;
input_set_capability ( dev , EV_KEY , BTN_TOOL_TRIPLETAP ) ;
2008-11-20 15:24:42 -05:00
}
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_FOUR_BUTTON ( info - > capabilities ) | |
SYN_CAP_MIDDLE_BUTTON ( info - > capabilities ) ) {
2017-04-03 16:37:24 -07:00
input_set_capability ( dev , EV_KEY , BTN_FORWARD ) ;
input_set_capability ( dev , EV_KEY , BTN_BACK ) ;
2005-04-16 15:20:36 -07:00
}
2017-03-05 15:51:33 -08:00
if ( ! SYN_CAP_EXT_BUTTONS_STICK ( info - > ext_cap_10 ) )
for ( i = 0 ; i < SYN_CAP_MULTI_BUTTON_NO ( info - > ext_cap ) ; i + + )
2017-04-03 16:37:24 -07:00
input_set_capability ( dev , EV_KEY , BTN_0 + i ) ;
2009-06-10 23:27:24 -07:00
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_CLICKPAD ( info - > ext_cap_0c ) ) {
2010-12-16 09:52:23 +01:00
__set_bit ( INPUT_PROP_BUTTONPAD , dev - > propbit ) ;
2015-03-08 22:34:50 -07:00
if ( psmouse_matches_pnp_id ( psmouse , topbuttonpad_pnp_ids ) & &
2017-03-05 15:51:33 -08:00
! SYN_CAP_EXT_BUTTONS_STICK ( info - > ext_cap_10 ) )
2014-05-19 22:53:23 -07:00
__set_bit ( INPUT_PROP_TOPBUTTONPAD , dev - > propbit ) ;
2010-04-19 10:37:21 -07:00
}
2017-04-03 16:54:04 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2011-11-08 00:00:35 -08:00
static ssize_t synaptics_show_disable_gesture ( struct psmouse * psmouse ,
void * data , char * buf )
{
struct synaptics_data * priv = psmouse - > private ;
return sprintf ( buf , " %c \n " , priv - > disable_gesture ? ' 1 ' : ' 0 ' ) ;
}
static ssize_t synaptics_set_disable_gesture ( struct psmouse * psmouse ,
void * data , const char * buf ,
size_t len )
{
struct synaptics_data * priv = psmouse - > private ;
unsigned int value ;
int err ;
err = kstrtouint ( buf , 10 , & value ) ;
if ( err )
return err ;
if ( value > 1 )
return - EINVAL ;
if ( value = = priv - > disable_gesture )
return len ;
priv - > disable_gesture = value ;
if ( value )
priv - > mode | = SYN_BIT_DISABLE_GESTURE ;
else
priv - > mode & = ~ SYN_BIT_DISABLE_GESTURE ;
if ( synaptics_mode_cmd ( psmouse , priv - > mode ) )
return - EIO ;
return len ;
}
PSMOUSE_DEFINE_ATTR ( disable_gesture , S_IWUSR | S_IRUGO , NULL ,
synaptics_show_disable_gesture ,
synaptics_set_disable_gesture ) ;
2005-04-16 15:20:36 -07:00
static void synaptics_disconnect ( struct psmouse * psmouse )
{
2011-11-08 00:00:35 -08:00
struct synaptics_data * priv = psmouse - > private ;
2017-03-02 14:13:53 -08:00
/*
* We might have left a breadcrumb when trying to
* set up SMbus companion.
*/
psmouse_smbus_cleanup ( psmouse ) ;
2017-03-05 15:51:33 -08:00
if ( ! priv - > absolute_mode & &
SYN_ID_DISGEST_SUPPORTED ( priv - > info . identity ) )
2011-11-08 00:00:35 -08:00
device_remove_file ( & psmouse - > ps2dev . serio - > dev ,
& psmouse_attr_disable_gesture . dattr ) ;
2005-04-16 15:20:36 -07:00
synaptics_reset ( psmouse ) ;
2011-11-08 00:00:35 -08:00
kfree ( priv ) ;
2005-04-16 15:20:36 -07:00
psmouse - > private = NULL ;
}
static int synaptics_reconnect ( struct psmouse * psmouse )
{
struct synaptics_data * priv = psmouse - > private ;
2017-03-05 15:51:33 -08:00
struct synaptics_device_info info ;
2017-03-24 11:20:38 -07:00
u8 param [ 2 ] ;
2011-01-28 22:05:14 -08:00
int retry = 0 ;
int error ;
2005-04-16 15:20:36 -07:00
2011-01-28 22:05:14 -08:00
do {
psmouse_reset ( psmouse ) ;
2011-12-12 00:05:53 -08:00
if ( retry ) {
/*
* On some boxes, right after resuming, the touchpad
* needs some time to finish initializing (I assume
* it needs time to calibrate) and start responding
* to Synaptics-specific queries, so let's wait a
* bit.
*/
ssleep ( 1 ) ;
}
2013-06-04 09:30:55 -07:00
ps2_command ( & psmouse - > ps2dev , param , PSMOUSE_CMD_GETID ) ;
2011-01-28 22:05:14 -08:00
error = synaptics_detect ( psmouse , 0 ) ;
} while ( error & & + + retry < 3 ) ;
2009-02-28 12:51:01 -08:00
2011-01-28 22:05:14 -08:00
if ( error )
2017-03-23 18:38:14 -07:00
return error ;
2005-04-16 15:20:36 -07:00
2011-01-28 22:05:14 -08:00
if ( retry > 1 )
2011-10-10 18:27:03 -07:00
psmouse_dbg ( psmouse , " reconnected after %d tries \n " , retry ) ;
2011-01-28 22:05:14 -08:00
2017-03-23 18:38:14 -07:00
error = synaptics_query_hardware ( psmouse , & info ) ;
if ( error ) {
2011-10-10 18:27:03 -07:00
psmouse_err ( psmouse , " Unable to query device. \n " ) ;
2017-03-23 18:38:14 -07:00
return error ;
2005-04-16 15:20:36 -07:00
}
2017-03-23 18:38:14 -07:00
error = synaptics_set_mode ( psmouse ) ;
if ( error ) {
2011-10-10 18:27:03 -07:00
psmouse_err ( psmouse , " Unable to initialize device. \n " ) ;
2017-03-23 18:38:14 -07:00
return error ;
2005-04-16 15:20:36 -07:00
}
2017-03-05 15:51:33 -08:00
if ( info . identity ! = priv - > info . identity | |
info . model_id ! = priv - > info . model_id | |
info . capabilities ! = priv - > info . capabilities | |
info . ext_cap ! = priv - > info . ext_cap ) {
2011-10-10 18:27:03 -07:00
psmouse_err ( psmouse ,
2017-03-05 15:51:33 -08:00
" hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x). \n " ,
priv - > info . identity , info . identity ,
priv - > info . model_id , info . model_id ,
priv - > info . capabilities , info . capabilities ,
priv - > info . ext_cap , info . ext_cap ) ;
2017-03-23 18:38:14 -07:00
return - ENXIO ;
2011-01-28 22:05:14 -08:00
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-12-03 23:21:14 -08:00
static bool impaired_toshiba_kbc ;
2013-08-12 11:05:58 -07:00
static const struct dmi_system_id toshiba_dmi_table [ ] __initconst = {
2009-12-03 23:21:14 -08:00
# if defined(CONFIG_DMI) && defined(CONFIG_X86)
2005-04-16 15:20:36 -07:00
{
2009-12-04 10:24:20 -08:00
/* Toshiba Satellite */
2005-04-16 15:20:36 -07:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " TOSHIBA " ) ,
2006-04-02 00:10:18 -05:00
DMI_MATCH ( DMI_PRODUCT_NAME , " Satellite " ) ,
2005-04-16 15:20:36 -07:00
} ,
} ,
2005-07-11 01:07:20 -05:00
{
2009-12-04 10:24:20 -08:00
/* Toshiba Dynabook */
2005-07-11 01:07:20 -05:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " TOSHIBA " ) ,
2006-04-02 00:10:18 -05:00
DMI_MATCH ( DMI_PRODUCT_NAME , " dynabook " ) ,
} ,
} ,
{
2009-12-04 10:24:20 -08:00
/* Toshiba Portege M300 */
2006-04-02 00:10:18 -05:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " TOSHIBA " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " PORTEGE M300 " ) ,
2005-07-11 01:07:20 -05:00
} ,
2009-10-12 21:35:00 -07:00
} ,
{
2009-12-04 10:24:20 -08:00
/* Toshiba Portege M300 */
2009-10-12 21:35:00 -07:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " TOSHIBA " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Portable PC " ) ,
DMI_MATCH ( DMI_PRODUCT_VERSION , " Version 1.0 " ) ,
} ,
2005-07-11 01:07:20 -05:00
} ,
2005-04-16 15:20:36 -07:00
# endif
2011-03-31 00:01:58 -07:00
{ }
2009-12-03 23:21:14 -08:00
} ;
2010-12-23 01:19:38 -08:00
static bool broken_olpc_ec ;
2013-08-12 11:05:58 -07:00
static const struct dmi_system_id olpc_dmi_table [ ] __initconst = {
2010-12-23 01:19:38 -08:00
# if defined(CONFIG_DMI) && defined(CONFIG_OLPC)
{
/* OLPC XO-1 or XO-1.5 */
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " OLPC " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " XO " ) ,
} ,
} ,
# endif
2011-03-31 00:01:58 -07:00
{ }
2010-12-23 01:19:38 -08:00
} ;
2014-07-14 10:26:56 -07:00
static const struct dmi_system_id __initconst cr48_dmi_table [ ] = {
# if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
/* Cr-48 Chromebook (Codename Mario) */
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " IEC " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Mario " ) ,
} ,
} ,
# endif
{ }
} ;
2009-12-03 23:21:14 -08:00
void __init synaptics_module_init ( void )
{
impaired_toshiba_kbc = dmi_check_system ( toshiba_dmi_table ) ;
2010-12-23 01:19:38 -08:00
broken_olpc_ec = dmi_check_system ( olpc_dmi_table ) ;
2014-07-14 10:26:56 -07:00
cr48_profile_sensor = dmi_check_system ( cr48_dmi_table ) ;
2009-12-03 23:21:14 -08:00
}
2005-04-16 15:20:36 -07:00
2017-03-02 14:13:53 -08:00
static int synaptics_init_ps2 ( struct psmouse * psmouse ,
struct synaptics_device_info * info ,
bool absolute_mode )
2005-04-16 15:20:36 -07:00
{
struct synaptics_data * priv ;
2017-03-02 14:13:53 -08:00
int err ;
2005-04-16 15:20:36 -07:00
2017-03-02 14:13:53 -08:00
synaptics_apply_quirks ( psmouse , info ) ;
2010-12-23 01:19:38 -08:00
2006-03-14 00:09:16 -05:00
psmouse - > private = priv = kzalloc ( sizeof ( struct synaptics_data ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! priv )
2010-09-29 18:53:35 -07:00
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
2017-03-02 14:13:53 -08:00
priv - > info = * info ;
2011-11-08 00:00:35 -08:00
priv - > absolute_mode = absolute_mode ;
2017-03-05 15:51:33 -08:00
if ( SYN_ID_DISGEST_SUPPORTED ( info - > identity ) )
2011-11-08 00:00:35 -08:00
priv - > disable_gesture = true ;
2005-04-16 15:20:36 -07:00
2014-12-29 14:43:44 -08:00
/*
* Unfortunately ForcePad capability is not exported over PS/2,
* so we have to resort to checking PNP IDs.
*/
priv - > is_forcepad = psmouse_matches_pnp_id ( psmouse , forcepad_pnp_ids ) ;
2017-03-02 14:13:53 -08:00
err = synaptics_set_mode ( psmouse ) ;
if ( err ) {
2011-11-08 00:00:35 -08:00
psmouse_err ( psmouse , " Unable to initialize device. \n " ) ;
2010-12-21 18:11:25 +01:00
goto init_fail ;
}
2017-03-05 15:51:33 -08:00
priv - > pkt_type = SYN_MODEL_NEWABS ( info - > model_id ) ?
SYN_NEWABS : SYN_OLDABS ;
2005-04-16 15:20:36 -07:00
2011-10-10 18:27:03 -07:00
psmouse_info ( psmouse ,
2017-03-23 14:56:06 -07:00
" Touchpad model: %lu, fw: %lu.%lu, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u \n " ,
2017-03-05 15:51:33 -08:00
SYN_ID_MODEL ( info - > identity ) ,
SYN_ID_MAJOR ( info - > identity ) , SYN_ID_MINOR ( info - > identity ) ,
info - > model_id ,
info - > capabilities , info - > ext_cap , info - > ext_cap_0c ,
info - > ext_cap_10 , info - > board_id , info - > firmware_id ) ;
2005-05-28 02:12:18 -05:00
2017-04-03 16:54:04 -07:00
err = set_input_params ( psmouse , priv ) ;
if ( err ) {
psmouse_err ( psmouse ,
" failed to set up capabilities: %d \n " , err ) ;
goto init_fail ;
}
2005-04-16 15:20:36 -07:00
2007-04-12 01:30:41 -04:00
/*
* Encode touchpad model so that it can be used to set
* input device->id.version and be visible to userspace.
* Because version is __u16 we have to drop something.
* Hardware info bits seem to be good candidates as they
* are documented to be for Synaptics corp. internal use.
*/
2017-03-05 15:51:33 -08:00
psmouse - > model = ( ( info - > model_id & 0x00ff0000 ) > > 8 ) |
( info - > model_id & 0x000000ff ) ;
2007-04-12 01:30:41 -04:00
2011-11-08 00:00:35 -08:00
if ( absolute_mode ) {
psmouse - > protocol_handler = synaptics_process_byte ;
psmouse - > pktsize = 6 ;
} else {
/* Relative mode follows standard PS/2 mouse protocol */
psmouse - > protocol_handler = psmouse_process_byte ;
psmouse - > pktsize = 3 ;
}
2005-04-16 15:20:36 -07:00
psmouse - > set_rate = synaptics_set_rate ;
psmouse - > disconnect = synaptics_disconnect ;
psmouse - > reconnect = synaptics_reconnect ;
2007-02-18 01:40:24 -05:00
psmouse - > cleanup = synaptics_reset ;
2006-01-14 00:27:37 -05:00
/* Synaptics can usually stay in sync without extra help */
psmouse - > resync_time = 0 ;
2005-04-16 15:20:36 -07:00
2017-03-05 15:51:33 -08:00
if ( SYN_CAP_PASS_THROUGH ( info - > capabilities ) )
2005-04-16 15:20:36 -07:00
synaptics_pt_create ( psmouse ) ;
/*
* Toshiba's KBC seems to have trouble handling data from
2010-12-23 01:18:28 -08:00
* Synaptics at full rate. Switch to a lower rate (roughly
* the same rate as a standard PS/2 mouse).
2005-04-16 15:20:36 -07:00
*/
2009-12-03 23:21:14 -08:00
if ( psmouse - > rate > = 80 & & impaired_toshiba_kbc ) {
2011-10-10 18:27:03 -07:00
psmouse_info ( psmouse ,
" Toshiba %s detected, limiting rate to 40pps. \n " ,
dmi_get_system_info ( DMI_PRODUCT_NAME ) ) ;
2005-04-16 15:20:36 -07:00
psmouse - > rate = 40 ;
}
2017-03-05 15:51:33 -08:00
if ( ! priv - > absolute_mode & & SYN_ID_DISGEST_SUPPORTED ( info - > identity ) ) {
2011-11-08 00:00:35 -08:00
err = device_create_file ( & psmouse - > ps2dev . serio - > dev ,
& psmouse_attr_disable_gesture . dattr ) ;
if ( err ) {
psmouse_err ( psmouse ,
" Failed to create disable_gesture attribute (%d) " ,
err ) ;
goto init_fail ;
}
}
2005-04-16 15:20:36 -07:00
return 0 ;
init_fail :
kfree ( priv ) ;
2011-11-08 00:00:35 -08:00
return err ;
}
2017-03-02 14:13:53 -08:00
static int __synaptics_init ( struct psmouse * psmouse , bool absolute_mode )
{
struct synaptics_device_info info ;
int error ;
psmouse_reset ( psmouse ) ;
error = synaptics_query_hardware ( psmouse , & info ) ;
if ( error ) {
psmouse_err ( psmouse , " Unable to query device: %d \n " , error ) ;
return error ;
}
return synaptics_init_ps2 ( psmouse , & info , absolute_mode ) ;
}
int synaptics_init_absolute ( struct psmouse * psmouse )
2011-11-08 00:00:35 -08:00
{
return __synaptics_init ( psmouse , true ) ;
}
int synaptics_init_relative ( struct psmouse * psmouse )
{
return __synaptics_init ( psmouse , false ) ;
2005-04-16 15:20:36 -07:00
}
2017-03-02 14:13:53 -08:00
static int synaptics_setup_ps2 ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
{
bool absolute_mode = true ;
int error ;
/*
* The OLPC XO has issues with Synaptics' absolute mode; the constant
* packet spew overloads the EC such that key presses on the keyboard
* are missed. Given that, don't even attempt to use Absolute mode.
* Relative mode seems to work just fine.
*/
if ( broken_olpc_ec ) {
psmouse_info ( psmouse ,
" OLPC XO detected, forcing relative protocol. \n " ) ;
absolute_mode = false ;
}
error = synaptics_init_ps2 ( psmouse , info , absolute_mode ) ;
if ( error )
return error ;
return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE ;
}
2007-03-10 01:39:54 -05:00
# else /* CONFIG_MOUSE_PS2_SYNAPTICS */
2009-12-03 23:21:14 -08:00
void __init synaptics_module_init ( void )
{
}
2017-03-02 14:13:53 -08:00
static int __maybe_unused
synaptics_setup_ps2 ( struct psmouse * psmouse ,
struct synaptics_device_info * info )
2007-03-10 01:39:54 -05:00
{
return - ENOSYS ;
}
# endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
2017-03-02 14:13:53 -08:00
# ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
/*
* The newest Synaptics device can use a secondary bus (called InterTouch) which
* provides a better bandwidth and allow a better control of the touchpads.
* This is used to decide if we need to use this bus or not.
*/
enum {
SYNAPTICS_INTERTOUCH_NOT_SET = - 1 ,
SYNAPTICS_INTERTOUCH_OFF ,
SYNAPTICS_INTERTOUCH_ON ,
} ;
2017-05-23 14:15:24 -07:00
static int synaptics_intertouch = IS_ENABLED ( CONFIG_RMI4_SMB ) ?
SYNAPTICS_INTERTOUCH_NOT_SET : SYNAPTICS_INTERTOUCH_OFF ;
2017-03-02 14:13:53 -08:00
module_param_named ( synaptics_intertouch , synaptics_intertouch , int , 0644 ) ;
MODULE_PARM_DESC ( synaptics_intertouch , " Use a secondary bus for the Synaptics device. " ) ;
static int synaptics_create_intertouch ( struct psmouse * psmouse ,
struct synaptics_device_info * info ,
bool leave_breadcrumbs )
{
bool topbuttonpad =
psmouse_matches_pnp_id ( psmouse , topbuttonpad_pnp_ids ) & &
! SYN_CAP_EXT_BUTTONS_STICK ( info - > ext_cap_10 ) ;
const struct rmi_device_platform_data pdata = {
. sensor_pdata = {
. sensor_type = rmi_sensor_touchpad ,
. axis_align . flip_y = true ,
2017-10-09 20:51:05 -07:00
. kernel_tracking = false ,
2017-03-02 14:13:53 -08:00
. topbuttonpad = topbuttonpad ,
} ,
2020-10-04 19:42:47 -07:00
. gpio_data = {
2017-03-02 14:13:53 -08:00
. buttonpad = SYN_CAP_CLICKPAD ( info - > ext_cap_0c ) ,
. trackstick_buttons =
! ! SYN_CAP_EXT_BUTTONS_STICK ( info - > ext_cap_10 ) ,
} ,
} ;
const struct i2c_board_info intertouch_board = {
I2C_BOARD_INFO ( " rmi4_smbus " , 0x2c ) ,
. flags = I2C_CLIENT_HOST_NOTIFY ,
} ;
return psmouse_smbus_init ( psmouse , & intertouch_board ,
2018-05-22 17:30:31 -07:00
& pdata , sizeof ( pdata ) , true ,
2017-03-02 14:13:53 -08:00
leave_breadcrumbs ) ;
}
2020-11-19 19:15:44 -08:00
/*
2017-03-02 14:13:53 -08:00
* synaptics_setup_intertouch - called once the PS/2 devices are enumerated
* and decides to instantiate a SMBus InterTouch device.
*/
static int synaptics_setup_intertouch ( struct psmouse * psmouse ,
struct synaptics_device_info * info ,
bool leave_breadcrumbs )
{
int error ;
if ( synaptics_intertouch = = SYNAPTICS_INTERTOUCH_OFF )
return - ENXIO ;
if ( synaptics_intertouch = = SYNAPTICS_INTERTOUCH_NOT_SET ) {
if ( ! psmouse_matches_pnp_id ( psmouse , topbuttonpad_pnp_ids ) & &
2017-05-26 16:51:19 -07:00
! psmouse_matches_pnp_id ( psmouse , smbus_pnp_ids ) ) {
if ( ! psmouse_matches_pnp_id ( psmouse , forcepad_pnp_ids ) )
psmouse_info ( psmouse ,
" Your touchpad (%s) says it can support a different bus. "
" If i2c-hid and hid-rmi are not used, you might want to try setting psmouse.synaptics_intertouch to 1 and report this to linux-input@vger.kernel.org. \n " ,
psmouse - > ps2dev . serio - > firmware_id ) ;
2017-03-02 14:13:53 -08:00
return - ENXIO ;
2017-05-26 16:51:19 -07:00
}
2017-03-02 14:13:53 -08:00
}
psmouse_info ( psmouse , " Trying to set up SMBus access \n " ) ;
error = synaptics_create_intertouch ( psmouse , info , leave_breadcrumbs ) ;
if ( error ) {
if ( error = = - EAGAIN )
psmouse_info ( psmouse , " SMbus companion is not ready yet \n " ) ;
else
psmouse_err ( psmouse , " unable to create intertouch device \n " ) ;
return error ;
}
return 0 ;
}
int synaptics_init_smbus ( struct psmouse * psmouse )
{
struct synaptics_device_info info ;
int error ;
psmouse_reset ( psmouse ) ;
error = synaptics_query_hardware ( psmouse , & info ) ;
if ( error ) {
psmouse_err ( psmouse , " Unable to query device: %d \n " , error ) ;
return error ;
}
if ( ! SYN_CAP_INTERTOUCH ( info . ext_cap_0c ) )
return - ENXIO ;
return synaptics_create_intertouch ( psmouse , & info , false ) ;
}
# else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
static int __maybe_unused
synaptics_setup_intertouch ( struct psmouse * psmouse ,
struct synaptics_device_info * info ,
bool leave_breadcrumbs )
{
return - ENOSYS ;
}
int synaptics_init_smbus ( struct psmouse * psmouse )
{
return - ENOSYS ;
}
# endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
# if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
int synaptics_init ( struct psmouse * psmouse )
{
struct synaptics_device_info info ;
int error ;
int retval ;
psmouse_reset ( psmouse ) ;
error = synaptics_query_hardware ( psmouse , & info ) ;
if ( error ) {
psmouse_err ( psmouse , " Unable to query device: %d \n " , error ) ;
return error ;
}
if ( SYN_CAP_INTERTOUCH ( info . ext_cap_0c ) ) {
2017-05-26 16:21:36 -07:00
if ( ( ! IS_ENABLED ( CONFIG_RMI4_SMB ) | |
! IS_ENABLED ( CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS ) ) & &
/* Forcepads need F21, which is not ready */
! psmouse_matches_pnp_id ( psmouse , forcepad_pnp_ids ) ) {
psmouse_warn ( psmouse ,
" The touchpad can support a better bus than the too old PS/2 protocol. "
" Make sure MOUSE_PS2_SYNAPTICS_SMBUS and RMI4_SMB are enabled to get a better touchpad experience. \n " ) ;
}
2017-03-02 14:13:53 -08:00
error = synaptics_setup_intertouch ( psmouse , & info , true ) ;
if ( ! error )
return PSMOUSE_SYNAPTICS_SMBUS ;
}
retval = synaptics_setup_ps2 ( psmouse , & info ) ;
if ( retval < 0 ) {
/*
* Not using any flavor of Synaptics support, so clean up
* SMbus breadcrumbs, if any.
*/
psmouse_smbus_cleanup ( psmouse ) ;
}
return retval ;
}
# else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
int synaptics_init ( struct psmouse * psmouse )
{
return - ENOSYS ;
}
# endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */