Recently there have been multiple patches to disable the ideapad-laptop's touchpad control code, because it is causing issues on various laptops, see the Fixes tags for this commit. The turning on/off of the ps2 aux port was added specifically for the IdeaPad Z570, where the EC does toggle the touchpad on/off LED and toggles the value returned by reading VPCCMD_R_TOUCHPAD, but it does not actually turn on/off the touchpad. The ideapad-laptop code really should not be messing with the i8042 controller on all devices just for this special case. Drop the touchpad_ctrl_via_ec flag and add a DMI based allow-list for devices which need this workaround, populating it with just the Ideapad Z570 for now. A related problem is that on recent Ideapad models the EC does not control the touchpad at all. Check for this by checking if the value read from VPCCMD_R_TOUCHPAD actually changes when receiving a touchpad-toggle hotkey event; and if it does not change send KEY_TOUCHPAD_TOGGLE to userspace to let userspace enable/disable the touchpad in software. Fixes: d69cd7eea93e ("platform/x86: ideapad-laptop: Disable touchpad_switch for ELAN0634") Fixes: a231224a601c ("platform/x86: ideapad-laptop: Disable touchpad_switch") Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> --- drivers/platform/x86/ideapad-laptop.c | 69 ++++++++++++++------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index eb0b1ec32c13..2654e3668c1a 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -134,6 +134,7 @@ struct ideapad_private { struct ideapad_dytc_priv *dytc; struct dentry *debug; unsigned long cfg; + unsigned long r_touchpad_val; struct { bool conservation_mode : 1; bool dytc : 1; @@ -142,8 +143,8 @@ struct ideapad_private { bool set_fn_lock_led : 1; bool hw_rfkill_switch : 1; bool kbd_bl : 1; - bool touchpad_ctrl_via_ec : 1; bool usb_charging : 1; + bool ctrl_ps2_aux_port : 1; } features; struct { bool initialized; @@ -174,6 +175,12 @@ MODULE_PARM_DESC(set_fn_lock_led, "Enable driver based updates of the fn-lock LED on fn-lock changes. " "If you need this please report this to: platform-driver-x86@xxxxxxxxxxxxxxx"); +static bool ctrl_ps2_aux_port; +module_param(ctrl_ps2_aux_port, bool, 0444); +MODULE_PARM_DESC(ctrl_ps2_aux_port, + "Enable driver based PS/2 aux port en-/dis-abling on touchpad on/off toggle. " + "If you need this please report this to: platform-driver-x86@xxxxxxxxxxxxxxx"); + /* * shared data */ @@ -729,8 +736,6 @@ static umode_t ideapad_is_visible(struct kobject *kobj, supported = priv->features.fan_mode; else if (attr == &dev_attr_fn_lock.attr) supported = priv->features.fn_lock; - else if (attr == &dev_attr_touchpad.attr) - supported = priv->features.touchpad_ctrl_via_ec; else if (attr == &dev_attr_usb_charging.attr) supported = priv->features.usb_charging; @@ -1152,6 +1157,7 @@ static const struct key_entry ideapad_keymap[] = { { KE_KEY, 65, { KEY_PROG4 } }, { KE_KEY, 66, { KEY_TOUCHPAD_OFF } }, { KE_KEY, 67, { KEY_TOUCHPAD_ON } }, + { KE_KEY, 68, { KEY_TOUCHPAD_TOGGLE } }, { KE_KEY, 128, { KEY_ESC } }, /* @@ -1493,9 +1499,6 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_ unsigned char param; int ret; - if (!priv->features.touchpad_ctrl_via_ec) - return; - /* Without reading from EC touchpad LED doesn't switch state */ ret = read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value); if (ret) @@ -1504,15 +1507,26 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_ /* * Some IdeaPads don't really turn off touchpad - they only * switch the LED state. We (de)activate KBC AUX port to turn - * touchpad off and on. We send KEY_TOUCHPAD_OFF and - * KEY_TOUCHPAD_ON to not to get out of sync with LED + * the touchpad on and off. */ - i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); + if (priv->features.ctrl_ps2_aux_port) + i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE : I8042_CMD_AUX_DISABLE); if (send_events) { - ideapad_input_report(priv, value ? 67 : 66); - sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); + /* + * On older models the EC controls the touchpad and toggles it + * on/off itself, in this case we report KEY_TOUCHPAD_ON/_OFF. + * If the EC did not toggle, report KEY_TOUCHPAD_TOGGLE. + */ + if (value != priv->r_touchpad_val) { + ideapad_input_report(priv, value ? 67 : 66); + sysfs_notify(&priv->platform_device->dev.kobj, NULL, "touchpad"); + } else { + ideapad_input_report(priv, 68); + } } + + priv->r_touchpad_val = value; } static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) @@ -1615,19 +1629,18 @@ static const struct dmi_system_id hw_rfkill_list[] = { {} }; -static const struct dmi_system_id no_touchpad_switch_list[] = { - { - .ident = "Lenovo Yoga 3 Pro 1370", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"), - }, - }, +/* + * On some models the EC toggles the touchpad muted LED on touchpad toggle + * hotkey presses, but the EC does not actually disable the touchpad itself. + * On these models the driver needs to explicitly enable/disable the i8042 + * (PS/2) aux port. + */ +static const struct dmi_system_id ctrl_ps2_aux_port_list[] = { { - .ident = "ZhaoYang K4e-IML", + /* Lenovo Ideapad Z570 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ZhaoYang K4e-IML"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"), }, }, {} @@ -1642,14 +1655,8 @@ static void ideapad_check_features(struct ideapad_private *priv) set_fn_lock_led || dmi_check_system(set_fn_lock_led_list); priv->features.hw_rfkill_switch = hw_rfkill_switch || dmi_check_system(hw_rfkill_list); - - /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */ - if (acpi_dev_present("ELAN0634", NULL, -1)) - priv->features.touchpad_ctrl_via_ec = 0; - else if (dmi_check_system(no_touchpad_switch_list)) - priv->features.touchpad_ctrl_via_ec = 0; - else - priv->features.touchpad_ctrl_via_ec = 1; + priv->features.ctrl_ps2_aux_port = + ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list); if (!read_ec_data(handle, VPCCMD_R_FAN, &val)) priv->features.fan_mode = true; @@ -1834,10 +1841,6 @@ static int ideapad_acpi_add(struct platform_device *pdev) if (!priv->features.hw_rfkill_switch) write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1); - /* The same for Touchpad */ - if (!priv->features.touchpad_ctrl_via_ec) - write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, 1); - for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) ideapad_register_rfkill(priv, i); -- 2.38.1