diff options
Diffstat (limited to 'drivers/input/mouse/synaptics.c')
-rw-r--r-- | drivers/input/mouse/synaptics.c | 554 |
1 files changed, 384 insertions, 170 deletions
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 0e08e2d497e9..d494c6c6aadc 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -29,6 +29,8 @@ #include <linux/input/mt.h> #include <linux/serio.h> #include <linux/libps2.h> +#include <linux/rmi.h> +#include <linux/i2c.h> #include <linux/slab.h> #include "psmouse.h" #include "synaptics.h" @@ -119,59 +121,8 @@ void synaptics_reset(struct psmouse *psmouse) synaptics_mode_cmd(psmouse, 0); } -#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 - }, - { } -}; +#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \ + defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS) /* This list has been kindly provided by Synaptics. */ static const char * const topbuttonpad_pnp_ids[] = { @@ -211,37 +162,50 @@ static const char * const topbuttonpad_pnp_ids[] = { NULL }; -/* This list has been kindly provided by Synaptics. */ -static const char * const forcepad_pnp_ids[] = { - "SYN300D", - "SYN3014", +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 */ + "LEN200f", /* T450s */ NULL }; -/***************************************************************************** - * 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. + * Send a command to the synpatics touchpad by special commands */ -static int synaptics_invert_y(int y) +static int synaptics_send_cmd(struct psmouse *psmouse, + unsigned char c, unsigned char *param) { - return YMAX_NOMINAL + YMIN_NOMINAL - y; + int error; + + error = psmouse_sliced_command(psmouse, c); + if (error) + return error; + + error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO); + if (error) + return error; + + return 0; } /* - * Send a command to the synpatics touchpad by special commands + * Identify Touchpad + * See also the SYN_ID_* macros */ -static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) +static int synaptics_identify(struct psmouse *psmouse, + struct synaptics_device_info *info) { - if (psmouse_sliced_command(psmouse, c)) - return -1; - if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) - return -1; - return 0; + unsigned char id[3]; + int error; + + error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id); + if (error) + return error; + + info->identity = (id[0] << 16) | (id[1] << 8) | id[2]; + return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO; } /* @@ -262,6 +226,23 @@ static int synaptics_model_id(struct psmouse *psmouse, return 0; } +/* + * Read the firmware id from the touchpad + */ +static int synaptics_firmware_id(struct psmouse *psmouse, + struct synaptics_device_info *info) +{ + unsigned char fwid[3]; + int error; + + error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid); + if (error) + return error; + + info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2]; + return 0; +} + static int synaptics_more_extended_queries(struct psmouse *psmouse, struct synaptics_device_info *info) { @@ -303,23 +284,6 @@ static int synaptics_query_modes(struct psmouse *psmouse, } /* - * Read the firmware id from the touchpad - */ -static int synaptics_firmware_id(struct psmouse *psmouse, - struct synaptics_device_info *info) -{ - unsigned char fwid[3]; - int error; - - error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid); - if (error) - return error; - - info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2]; - return 0; -} - -/* * Read the capability-bits from the touchpad * see also the SYN_CAP_* macros */ @@ -380,28 +344,9 @@ static int synaptics_capability(struct psmouse *psmouse, } /* - * Identify Touchpad - * See also the SYN_ID_* macros - */ -static int synaptics_identify(struct psmouse *psmouse, - struct synaptics_device_info *info) -{ - unsigned char id[3]; - int error; - - error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id); - if (error) - return error; - - info->identity = (id[0] << 16) | (id[1] << 8) | id[2]; - return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO; -} - -/* * Read touchpad resolution and maximum reported coordinates * Resolution is left zero if touchpad does not support the query */ - static int synaptics_resolution(struct psmouse *psmouse, struct synaptics_device_info *info) { @@ -460,10 +405,118 @@ static int synaptics_resolution(struct psmouse *psmouse, return 0; } +static int synaptics_query_hardware(struct psmouse *psmouse, + struct synaptics_device_info *info) +{ + int error; + + 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; + + return 0; +} + +#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 + }, + { } +}; + +/* This list has been kindly provided by Synaptics. */ +static const char * const forcepad_pnp_ids[] = { + "SYN300D", + "SYN3014", + NULL +}; + +/***************************************************************************** + * 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) { @@ -494,38 +547,6 @@ static void synaptics_apply_quirks(struct psmouse *psmouse, } } -static int synaptics_query_hardware(struct psmouse *psmouse, - struct synaptics_device_info *info) -{ - int error; - - 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; - - return 0; -} - static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) { static unsigned char param = 0xc8; @@ -1335,6 +1356,12 @@ static void synaptics_disconnect(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; + /* + * We might have left a breadcrumb when trying to + * set up SMbus companion. + */ + psmouse_smbus_cleanup(psmouse); + if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->info.identity)) device_remove_file(&psmouse->ps2dev.serio->dev, @@ -1475,39 +1502,20 @@ void __init synaptics_module_init(void) cr48_profile_sensor = dmi_check_system(cr48_dmi_table); } -static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) +static int synaptics_init_ps2(struct psmouse *psmouse, + struct synaptics_device_info *info, + bool absolute_mode) { struct synaptics_data *priv; - struct synaptics_device_info *info; - int err = -1; + int err; - /* - * 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 (absolute_mode && broken_olpc_ec) { - psmouse_info(psmouse, - "OLPC XO detected, not enabling Synaptics protocol.\n"); - return -ENODEV; - } + synaptics_apply_quirks(psmouse, info); psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) return -ENOMEM; - info = &priv->info; - - psmouse_reset(psmouse); - - if (synaptics_query_hardware(psmouse, info)) { - psmouse_err(psmouse, "Unable to query device.\n"); - goto init_fail; - } - - synaptics_apply_quirks(psmouse, info); - + priv->info = *info; priv->absolute_mode = absolute_mode; if (SYN_ID_DISGEST_SUPPORTED(info->identity)) priv->disable_gesture = true; @@ -1518,7 +1526,8 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) */ priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids); - if (synaptics_set_mode(psmouse)) { + err = synaptics_set_mode(psmouse); + if (err) { psmouse_err(psmouse, "Unable to initialize device.\n"); goto init_fail; } @@ -1595,7 +1604,23 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) return err; } -int synaptics_init(struct psmouse *psmouse) +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) { return __synaptics_init(psmouse, true); } @@ -1605,15 +1630,204 @@ int synaptics_init_relative(struct psmouse *psmouse) return __synaptics_init(psmouse, false); } +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; +} + #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ void __init synaptics_module_init(void) { } -int synaptics_init(struct psmouse *psmouse) +static int __maybe_unused +synaptics_setup_ps2(struct psmouse *psmouse, + struct synaptics_device_info *info) { return -ENOSYS; } #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ + +#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, +}; + +static int synaptics_intertouch = SYNAPTICS_INTERTOUCH_NOT_SET; +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, + /* to prevent cursors jumps: */ + .kernel_tracking = true, + .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, + &pdata, sizeof(pdata), + 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) && + !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids)) + return -ENXIO; + } + + 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)) { + 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 */ |