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)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* 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" ,
"LEN0049" ,
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 */
"LEN004a" , /* W541 */
2018-11-24 23:28:10 -08:00
"LEN005b" , /* P50 */
2018-12-08 20:13:35 -08:00
"LEN005e" , /* T560 */
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) */
2018-02-03 11:49:22 -08:00
"LEN0092" , /* X1 Carbon 6 */
"LEN0096" , /* X280 */
2018-05-22 17:16:08 -07:00
"LEN0097" , /* X280 -> ALPS trackpoint */
2017-03-02 14:13:53 -08:00
"LEN200f" , /* T450s */
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 */
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
/*
* Send a command to the synpatics touchpad by special commands
*/
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 ;
strlcpy ( serio -> name , "Synaptics pass-through" , sizeof ( serio -> name ));
strlcpy ( serio -> phys , "synaptics-pt/serio0" , sizeof ( serio -> name ));
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 :
2017-03-05 15:51:33 -08:00
if ( SYN_MODEL_PEN ( info -> model_id ))
2005-04-16 15:20:36 -07:00
; /* Nothing, treat a pen as a single finger */
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 ,
},
. f30_data = {
. 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 );
}
/**
* 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 */