platform/x86: ideapad-laptop: move ymc_trigger_ec from lenovo-ymc
[ Upstream commit cde7886b35176d56e72bfc68dc104fa08e7b072c ] Some models need to trigger the EC after each YMC event for the yoga mode control to work properly. EC triggering consist of a VPC call from the lenovo-ymc module. Except for this, all VPC calls are in the ideapad-laptop module. Since ideapad-laptop has a notification chain, a new YMC_EVENT action can be added and triggered from the lenovo-ymc module. Then the ideapad-laptop can trigger the EC. If the triggering is in the ideapad-laptop module, then the ec_trigger module parameter should be there as well. Move the ymc_trigger_ec functionality and the ec_trigger module parameter to the ideapad-laptop module. Signed-off-by: Gergo Koteles <soyer@irl.hu> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Link: https://lore.kernel.org/r/d980ab3ac32b5e554f456b0ff17279bfdbe2a203.1721898747.git.soyer@irl.hu Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Stable-dep-of: 5808c3421695 ("platform/x86: ideapad-laptop: use usleep_range() for EC polling") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
d19ae7b033
commit
f8761b11f1
@@ -466,6 +466,7 @@ config LENOVO_YMC
|
|||||||
tristate "Lenovo Yoga Tablet Mode Control"
|
tristate "Lenovo Yoga Tablet Mode Control"
|
||||||
depends on ACPI_WMI
|
depends on ACPI_WMI
|
||||||
depends on INPUT
|
depends on INPUT
|
||||||
|
depends on IDEAPAD_LAPTOP
|
||||||
select INPUT_SPARSEKMAP
|
select INPUT_SPARSEKMAP
|
||||||
help
|
help
|
||||||
This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input
|
This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input
|
||||||
|
@@ -145,6 +145,7 @@ struct ideapad_private {
|
|||||||
bool touchpad_ctrl_via_ec : 1;
|
bool touchpad_ctrl_via_ec : 1;
|
||||||
bool ctrl_ps2_aux_port : 1;
|
bool ctrl_ps2_aux_port : 1;
|
||||||
bool usb_charging : 1;
|
bool usb_charging : 1;
|
||||||
|
bool ymc_ec_trigger : 1;
|
||||||
} features;
|
} features;
|
||||||
struct {
|
struct {
|
||||||
bool initialized;
|
bool initialized;
|
||||||
@@ -188,6 +189,12 @@ MODULE_PARM_DESC(touchpad_ctrl_via_ec,
|
|||||||
"Enable registering a 'touchpad' sysfs-attribute which can be used to manually "
|
"Enable registering a 'touchpad' sysfs-attribute which can be used to manually "
|
||||||
"tell the EC to enable/disable the touchpad. This may not work on all models.");
|
"tell the EC to enable/disable the touchpad. This may not work on all models.");
|
||||||
|
|
||||||
|
static bool ymc_ec_trigger __read_mostly;
|
||||||
|
module_param(ymc_ec_trigger, bool, 0444);
|
||||||
|
MODULE_PARM_DESC(ymc_ec_trigger,
|
||||||
|
"Enable EC triggering work-around to force emitting tablet mode events. "
|
||||||
|
"If you need this please report this to: platform-driver-x86@vger.kernel.org");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* shared data
|
* shared data
|
||||||
*/
|
*/
|
||||||
@@ -1501,10 +1508,50 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv, bool send_
|
|||||||
priv->r_touchpad_val = value;
|
priv->r_touchpad_val = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct dmi_system_id ymc_ec_trigger_quirk_dmi_table[] = {
|
||||||
|
{
|
||||||
|
/* Lenovo Yoga 7 14ARB7 */
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* Lenovo Yoga 7 14ACN6 */
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "82N7"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ideapad_laptop_trigger_ec(void)
|
||||||
|
{
|
||||||
|
struct ideapad_private *priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
guard(mutex)(&ideapad_shared_mutex);
|
||||||
|
|
||||||
|
priv = ideapad_shared;
|
||||||
|
if (!priv)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!priv->features.ymc_ec_trigger)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_YMC, 1);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(&priv->platform_device->dev, "Could not write YMC: %d\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
static int ideapad_laptop_nb_notify(struct notifier_block *nb,
|
static int ideapad_laptop_nb_notify(struct notifier_block *nb,
|
||||||
unsigned long action, void *data)
|
unsigned long action, void *data)
|
||||||
{
|
{
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
case IDEAPAD_LAPTOP_YMC_EVENT:
|
||||||
|
ideapad_laptop_trigger_ec();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1670,6 +1717,8 @@ static void ideapad_check_features(struct ideapad_private *priv)
|
|||||||
priv->features.ctrl_ps2_aux_port =
|
priv->features.ctrl_ps2_aux_port =
|
||||||
ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list);
|
ctrl_ps2_aux_port || dmi_check_system(ctrl_ps2_aux_port_list);
|
||||||
priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec;
|
priv->features.touchpad_ctrl_via_ec = touchpad_ctrl_via_ec;
|
||||||
|
priv->features.ymc_ec_trigger =
|
||||||
|
ymc_ec_trigger || dmi_check_system(ymc_ec_trigger_quirk_dmi_table);
|
||||||
|
|
||||||
if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
|
if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
|
||||||
priv->features.fan_mode = true;
|
priv->features.fan_mode = true;
|
||||||
|
@@ -14,6 +14,10 @@
|
|||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
|
|
||||||
|
enum ideapad_laptop_notifier_actions {
|
||||||
|
IDEAPAD_LAPTOP_YMC_EVENT,
|
||||||
|
};
|
||||||
|
|
||||||
int ideapad_laptop_register_notifier(struct notifier_block *nb);
|
int ideapad_laptop_register_notifier(struct notifier_block *nb);
|
||||||
int ideapad_laptop_unregister_notifier(struct notifier_block *nb);
|
int ideapad_laptop_unregister_notifier(struct notifier_block *nb);
|
||||||
void ideapad_laptop_call_notifier(unsigned long action, void *data);
|
void ideapad_laptop_call_notifier(unsigned long action, void *data);
|
||||||
|
@@ -20,32 +20,10 @@
|
|||||||
#define LENOVO_YMC_QUERY_INSTANCE 0
|
#define LENOVO_YMC_QUERY_INSTANCE 0
|
||||||
#define LENOVO_YMC_QUERY_METHOD 0x01
|
#define LENOVO_YMC_QUERY_METHOD 0x01
|
||||||
|
|
||||||
static bool ec_trigger __read_mostly;
|
|
||||||
module_param(ec_trigger, bool, 0444);
|
|
||||||
MODULE_PARM_DESC(ec_trigger, "Enable EC triggering work-around to force emitting tablet mode events");
|
|
||||||
|
|
||||||
static bool force;
|
static bool force;
|
||||||
module_param(force, bool, 0444);
|
module_param(force, bool, 0444);
|
||||||
MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type");
|
MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type");
|
||||||
|
|
||||||
static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = {
|
|
||||||
{
|
|
||||||
/* Lenovo Yoga 7 14ARB7 */
|
|
||||||
.matches = {
|
|
||||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
/* Lenovo Yoga 7 14ACN6 */
|
|
||||||
.matches = {
|
|
||||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "82N7"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct dmi_system_id allowed_chasis_types_dmi_table[] = {
|
static const struct dmi_system_id allowed_chasis_types_dmi_table[] = {
|
||||||
{
|
{
|
||||||
.matches = {
|
.matches = {
|
||||||
@@ -62,21 +40,8 @@ static const struct dmi_system_id allowed_chasis_types_dmi_table[] = {
|
|||||||
|
|
||||||
struct lenovo_ymc_private {
|
struct lenovo_ymc_private {
|
||||||
struct input_dev *input_dev;
|
struct input_dev *input_dev;
|
||||||
struct acpi_device *ec_acpi_dev;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void lenovo_ymc_trigger_ec(struct wmi_device *wdev, struct lenovo_ymc_private *priv)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (!priv->ec_acpi_dev)
|
|
||||||
return;
|
|
||||||
|
|
||||||
err = write_ec_cmd(priv->ec_acpi_dev->handle, VPCCMD_W_YMC, 1);
|
|
||||||
if (err)
|
|
||||||
dev_warn(&wdev->dev, "Could not write YMC: %d\n", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct key_entry lenovo_ymc_keymap[] = {
|
static const struct key_entry lenovo_ymc_keymap[] = {
|
||||||
/* Ignore the uninitialized state */
|
/* Ignore the uninitialized state */
|
||||||
{ KE_IGNORE, 0x00 },
|
{ KE_IGNORE, 0x00 },
|
||||||
@@ -127,11 +92,9 @@ static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data)
|
|||||||
|
|
||||||
free_obj:
|
free_obj:
|
||||||
kfree(obj);
|
kfree(obj);
|
||||||
lenovo_ymc_trigger_ec(wdev, priv);
|
ideapad_laptop_call_notifier(IDEAPAD_LAPTOP_YMC_EVENT, &code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acpi_dev_put_helper(void *p) { acpi_dev_put(p); }
|
|
||||||
|
|
||||||
static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
|
static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
|
||||||
{
|
{
|
||||||
struct lenovo_ymc_private *priv;
|
struct lenovo_ymc_private *priv;
|
||||||
@@ -145,29 +108,10 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
ec_trigger |= dmi_check_system(ec_trigger_quirk_dmi_table);
|
|
||||||
|
|
||||||
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (ec_trigger) {
|
|
||||||
pr_debug("Lenovo YMC enable EC triggering.\n");
|
|
||||||
priv->ec_acpi_dev = acpi_dev_get_first_match_dev("VPC2004", NULL, -1);
|
|
||||||
|
|
||||||
if (!priv->ec_acpi_dev) {
|
|
||||||
dev_err(&wdev->dev, "Could not find EC ACPI device.\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
err = devm_add_action_or_reset(&wdev->dev,
|
|
||||||
acpi_dev_put_helper, priv->ec_acpi_dev);
|
|
||||||
if (err) {
|
|
||||||
dev_err(&wdev->dev,
|
|
||||||
"Could not clean up EC ACPI device: %d\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input_dev = devm_input_allocate_device(&wdev->dev);
|
input_dev = devm_input_allocate_device(&wdev->dev);
|
||||||
if (!input_dev)
|
if (!input_dev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@@ -194,7 +138,6 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
|
|||||||
dev_set_drvdata(&wdev->dev, priv);
|
dev_set_drvdata(&wdev->dev, priv);
|
||||||
|
|
||||||
/* Report the state for the first time on probe */
|
/* Report the state for the first time on probe */
|
||||||
lenovo_ymc_trigger_ec(wdev, priv);
|
|
||||||
lenovo_ymc_notify(wdev, NULL);
|
lenovo_ymc_notify(wdev, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -219,3 +162,4 @@ module_wmi_driver(lenovo_ymc_driver);
|
|||||||
MODULE_AUTHOR("Gergo Koteles <soyer@irl.hu>");
|
MODULE_AUTHOR("Gergo Koteles <soyer@irl.hu>");
|
||||||
MODULE_DESCRIPTION("Lenovo Yoga Mode Control driver");
|
MODULE_DESCRIPTION("Lenovo Yoga Mode Control driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_IMPORT_NS(IDEAPAD_LAPTOP);
|
||||||
|
Reference in New Issue
Block a user