All of lore.kernel.org
 help / color / mirror / Atom feed
From: Angela Czubak <acz@semihalf.com>
To: linux-input@vger.kernel.org
Cc: upstream@semihalf.com, benjamin.tissoires@redhat.com,
	jikos@kernel.org, dmitry.torokhov@gmail.com,
	Angela Czubak <acz@semihalf.com>
Subject: [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook
Date: Fri, 13 May 2022 09:39:27 +0000	[thread overview]
Message-ID: <20220513093927.1632262-18-acz@semihalf.com> (raw)
In-Reply-To: <20220513093927.1632262-1-acz@semihalf.com>

If user closes the lid the touchscreen gets close to the touchpad surface,
which causes interference and makes the touchpad enter noise mode.
Right after opening the lid the cursor is unresponsive because of the mode
mentioned.
To fix this issue we switch the surface off and on so that Elan's FW
performs recalibration once the lid has been opened.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-multitouch.c | 220 ++++++++++++++++++++++++++++++++++-
 1 file changed, 219 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 2d1b8c400c2f..73e47fe7d773 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -32,11 +32,14 @@
  */
 
 #include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/input/mt.h>
 #include <linux/jiffies.h>
+#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
 
@@ -159,6 +162,7 @@ struct mt_report_data {
 };
 
 struct mt_device {
+	struct list_head list;	/* for list of devices needing input handler */
 	struct mt_class mtclass;	/* our mt device class */
 	struct timer_list release_timer;	/* to release sticky fingers */
 	struct hid_haptic_device *haptic;	/* haptic related configuration */
@@ -173,8 +177,15 @@ struct mt_device {
 
 	struct list_head applications;
 	struct list_head reports;
+
+	struct work_struct lid_work;
+	struct mutex mode_mutex;
+	bool lid_switch;
 };
 
+static struct workqueue_struct *mt_mode_wq;
+static LIST_HEAD(mt_devices_with_lid_handler);
+
 static void mt_post_parse_default_settings(struct mt_device *td,
 					   struct mt_application *app);
 static void mt_post_parse(struct mt_device *td, struct mt_application *app);
@@ -394,6 +405,91 @@ static const struct mt_class mt_classes[] = {
 	{ }
 };
 
+static void mt_input_lid_event(struct input_handle *handle, unsigned int type,
+			     unsigned int code, int value)
+{
+	struct mt_device *td, *n;
+
+	if (type == EV_SW && code == SW_LID && !value) {
+		list_for_each_entry_safe(td, n, &mt_devices_with_lid_handler, list)
+			queue_work(mt_mode_wq, &td->lid_work);
+	}
+}
+
+struct mt_input_lid {
+	struct input_handle handle;
+};
+
+static int mt_input_lid_connect(struct input_handler *handler,
+				struct input_dev *dev,
+				const struct input_device_id *id)
+{
+	struct mt_input_lid *lid;
+	char *name;
+	int error;
+
+	lid = kzalloc(sizeof(*lid), GFP_KERNEL);
+	if (!lid)
+		return -ENOMEM;
+
+	name = kasprintf(GFP_KERNEL, "hid-mt-lid-%s", dev_name(&dev->dev));
+	if (!name) {
+		error = -ENOMEM;
+		goto err_free_lid;
+	}
+
+	lid->handle.dev = dev;
+	lid->handle.handler = handler;
+	lid->handle.name = name;
+	lid->handle.private = lid;
+
+	error = input_register_handle(&lid->handle);
+	if (error)
+		goto err_free_name;
+
+	error = input_open_device(&lid->handle);
+	if (error)
+		goto err_unregister_handle;
+
+	return 0;
+
+err_unregister_handle:
+	input_unregister_handle(&lid->handle);
+err_free_name:
+	kfree(name);
+err_free_lid:
+	kfree(lid);
+	return error;
+}
+
+static void mt_input_lid_disconnect(struct input_handle *handle)
+{
+	struct mt_input_lid *lid = handle->private;
+
+	input_close_device(handle);
+	input_unregister_handle(handle);
+
+	kfree(handle->name);
+	kfree(lid);
+}
+
+static const struct input_device_id mt_input_lid_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
+		.evbit = { BIT_MASK(EV_SW) },
+		.swbit = { [BIT_WORD(SW_LID)] = BIT_MASK(SW_LID) },
+	},
+	{ },
+};
+
+static struct input_handler mt_input_lid_handler = {
+	.event =	mt_input_lid_event,
+	.connect =	mt_input_lid_connect,
+	.disconnect =	mt_input_lid_disconnect,
+	.name =		"hid-mt-lid",
+	.id_table =	mt_input_lid_ids,
+};
+
 static ssize_t mt_show_quirks(struct device *dev,
 			   struct device_attribute *attr,
 			   char *buf)
@@ -548,6 +644,83 @@ static struct mt_usages *mt_allocate_usage(struct hid_device *hdev,
 	return usage;
 }
 
+static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency,
+			 bool surface_switch, bool button_switch);
+
+static void lid_work_handler(struct work_struct *work)
+{
+
+	struct mt_device *td = container_of(work, struct mt_device,
+					    lid_work);
+	struct hid_device *hdev = td->hdev;
+
+	mutex_lock(&td->mode_mutex);
+	mt_set_modes(hdev, HID_LATENCY_NORMAL, false, false);
+	/* Elan's touchpad VID 323B needs this delay to handle both switch
+	 * surface off and switch surface on and trigger recalibration
+	 * properly.
+	 */
+	msleep(50);
+	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
+}
+
+static const struct dmi_system_id mt_lid_handler_dmi_table[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Redrix"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Redrix4ES"),
+		},
+	},
+	{}
+};
+
+static int mt_create_lid_handler(void)
+{
+	int error = 0;
+
+	if (!dmi_check_system(mt_lid_handler_dmi_table))
+		return 0;
+
+	mt_mode_wq = alloc_ordered_workqueue("hid-mt-lid", WQ_FREEZABLE);
+	if (mt_mode_wq == NULL)
+		return -ENOMEM;
+
+	error = input_register_handler(&mt_input_lid_handler);
+	if (error)
+		goto remove_wq;
+
+	return 0;
+
+remove_wq:
+	destroy_workqueue(mt_mode_wq);
+	mt_mode_wq = NULL;
+	return error;
+}
+
+static void mt_configure_lid_handler(struct mt_device *td)
+{
+	struct hid_device *hdev = td->hdev;
+
+	if (hdev->bus != BUS_I2C)
+		return;
+
+	td->lid_switch = true;
+	list_add_tail(&td->list, &mt_devices_with_lid_handler);
+}
+
+static void mt_destroy_lid_handler(void)
+{
+	input_unregister_handler(&mt_input_lid_handler);
+	destroy_workqueue(mt_mode_wq);
+}
+
 static struct mt_application *mt_allocate_application(struct mt_device *td,
 						      struct hid_report *report)
 {
@@ -571,6 +744,8 @@ static struct mt_application *mt_allocate_application(struct mt_device *td,
 	if (application == HID_DG_TOUCHPAD) {
 		mt_application->mt_flags |= INPUT_MT_POINTER;
 		td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+		if (mt_mode_wq)
+			mt_configure_lid_handler(td);
 	}
 
 	mt_application->scantime = DEFAULT_ZERO;
@@ -1767,6 +1942,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	INIT_LIST_HEAD(&td->applications);
 	INIT_LIST_HEAD(&td->reports);
 
+	INIT_LIST_HEAD(&td->list);
+	INIT_WORK(&td->lid_work, lid_work_handler);
+	mutex_init(&td->mode_mutex);
+
 	if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
 		td->serial_maybe = true;
 
@@ -1830,12 +2009,18 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
 	struct mt_device *td = hid_get_drvdata(hdev);
 	struct hid_haptic_device *haptic = td->haptic;
 
+	/* Wait for switch on completion */
+	if (td->lid_switch)
+		flush_workqueue(mt_mode_wq);
+
+	mutex_lock(&td->mode_mutex);
 	/* High latency is desirable for power savings during S3/S0ix */
 	if ((td->mtclass.quirks & MT_QUIRK_DISABLE_WAKEUP) ||
 	    !hid_hw_may_wakeup(hdev))
 		mt_set_modes(hdev, HID_LATENCY_HIGH, false, false);
 	else
 		mt_set_modes(hdev, HID_LATENCY_HIGH, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_suspend(hdev, haptic);
@@ -1849,7 +2034,10 @@ static int mt_reset_resume(struct hid_device *hdev)
 	struct hid_haptic_device *haptic = td->haptic;
 
 	mt_release_contacts(hdev);
+
+	mutex_lock(&td->mode_mutex);
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_resume(hdev, haptic);
@@ -1868,7 +2056,9 @@ static int mt_resume(struct hid_device *hdev)
 
 	hid_hw_idle(hdev, 0, 0, HID_REQ_SET_IDLE);
 
+	mutex_lock(&td->mode_mutex);
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_resume(hdev, haptic);
@@ -1883,7 +2073,9 @@ static int mt_reset(struct hid_device *hdev)
 	struct hid_haptic_device *haptic = td->haptic;
 
 	mt_release_contacts(hdev);
+	mutex_lock(&td->mode_mutex);
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_reset(hdev, haptic);
@@ -1899,6 +2091,8 @@ static void mt_remove(struct hid_device *hdev)
 
 	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
 	hid_hw_stop(hdev);
+
+	list_del(&td->list);
 }
 
 /*
@@ -2302,4 +2496,28 @@ static struct hid_driver mt_driver = {
 	.resume = mt_resume,
 #endif
 };
-module_hid_driver(mt_driver);
+
+static int __init mt_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&mt_driver);
+	if (ret)
+		return ret;
+
+	ret = mt_create_lid_handler();
+	if (ret)
+		hid_unregister_driver(&mt_driver);
+
+	return ret;
+}
+module_init(mt_init);
+
+static void __exit mt_exit(void)
+{
+	if (mt_mode_wq)
+		mt_destroy_lid_handler();
+
+	hid_unregister_driver(&mt_driver);
+}
+module_exit(mt_exit);
-- 
2.36.0.550.gb090851708-goog


  parent reply	other threads:[~2022-05-13  9:40 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
2022-05-13  9:39 ` [PATCH v3 01/17] HID: add haptics page defines Angela Czubak
2022-05-13  9:39 ` [PATCH v3 02/17] Input: add FF_HID effect type Angela Czubak
2022-05-13  9:39 ` [PATCH v3 03/17] HID: haptic: introduce hid_haptic_device Angela Czubak
2022-05-13  9:39 ` [PATCH v3 04/17] HID: input: allow mapping of haptic output Angela Czubak
2022-05-13  9:39 ` [PATCH v3 05/17] HID: haptic: initialize haptic device Angela Czubak
2022-05-13  9:39 ` [PATCH v3 06/17] Input: add shared effects Angela Czubak
2022-05-13  9:39 ` [PATCH v3 07/17] HID: haptic: implement release and press effects Angela Czubak
2022-05-13  9:39 ` [PATCH v3 08/17] HID: input: calculate resolution for pressure Angela Czubak
2022-05-13  9:39 ` [PATCH v3 09/17] HID: haptic: add functions handling events Angela Czubak
2022-05-13  9:39 ` [PATCH v3 10/17] Input: MT - add INPUT_MT_MAX_FORCE flags Angela Czubak
2022-05-13  9:39 ` [PATCH v3 11/17] HID: haptic: add hid_haptic_switch_mode Angela Czubak
2022-05-13  9:39 ` [PATCH v3 12/17] HID: multitouch: add haptic multitouch support Angela Czubak
2022-05-13  9:39 ` [PATCH v3 13/17] Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL Angela Czubak
2022-05-13  9:39 ` [PATCH v3 14/17] HID: haptic: add hid_haptic_change_control Angela Czubak
2022-05-13  9:39 ` [PATCH v3 15/17] HID: add HID device reset callback Angela Czubak
2022-05-13  9:39 ` [PATCH v3 16/17] HID: haptic: implement HID haptic " Angela Czubak
2022-05-13  9:39 ` Angela Czubak [this message]
2022-05-15  0:07   ` [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook marcoshalano
2022-05-22 17:55 ` [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
2022-05-23  6:57   ` Jiri Kosina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220513093927.1632262-18-acz@semihalf.com \
    --to=acz@semihalf.com \
    --cc=benjamin.tissoires@redhat.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=upstream@semihalf.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.