--- /dev/null
+From 94b179052f95c294d83e9c9c34f7833cf3cd4305 Mon Sep 17 00:00:00 2001
+From: Jason Gerecke <killertofu@gmail.com>
+Date: Fri, 15 Jul 2022 16:05:19 -0700
+Subject: HID: wacom: Force pen out of prox if no events have been received in a while
+
+From: Jason Gerecke <killertofu@gmail.com>
+
+commit 94b179052f95c294d83e9c9c34f7833cf3cd4305 upstream.
+
+Prox-out events may not be reliably sent by some AES firmware. This can
+cause problems for users, particularly due to arbitration logic disabling
+touch input while the pen is in prox.
+
+This commit adds a timer which is reset every time a new prox event is
+received. When the timer expires we check to see if the pen is still in
+prox and force it out if necessary. This is patterend off of the same
+solution used by 'hid-letsketch' driver which has a similar problem.
+
+Link: https://github.com/linuxwacom/input-wacom/issues/310
+Signed-off-by: Jason Gerecke <jason.gerecke@wacom.com>
+Signed-off-by: Jiri Kosina <jkosina@suse.cz>
+Cc: Ping Cheng <pinglinux@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/hid/wacom.h | 3 +++
+ drivers/hid/wacom_sys.c | 2 ++
+ drivers/hid/wacom_wac.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 44 insertions(+)
+
+--- a/drivers/hid/wacom.h
++++ b/drivers/hid/wacom.h
+@@ -94,6 +94,7 @@
+ #include <linux/leds.h>
+ #include <linux/usb/input.h>
+ #include <linux/power_supply.h>
++#include <linux/timer.h>
+ #include <asm/unaligned.h>
+
+ /*
+@@ -170,6 +171,7 @@ struct wacom {
+ struct delayed_work init_work;
+ struct wacom_remote *remote;
+ struct work_struct mode_change_work;
++ struct timer_list idleprox_timer;
+ bool generic_has_leds;
+ struct wacom_leds {
+ struct wacom_group_leds *groups;
+@@ -242,4 +244,5 @@ struct wacom_led *wacom_led_find(struct
+ struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur);
+ int wacom_equivalent_usage(int usage);
+ int wacom_initialize_leds(struct wacom *wacom);
++void wacom_idleprox_timeout(struct timer_list *list);
+ #endif
+--- a/drivers/hid/wacom_sys.c
++++ b/drivers/hid/wacom_sys.c
+@@ -2611,6 +2611,7 @@ static int wacom_probe(struct hid_device
+ INIT_WORK(&wacom->battery_work, wacom_battery_work);
+ INIT_WORK(&wacom->remote_work, wacom_remote_work);
+ INIT_WORK(&wacom->mode_change_work, wacom_mode_change_work);
++ timer_setup(&wacom->idleprox_timer, &wacom_idleprox_timeout, TIMER_DEFERRABLE);
+
+ /* ask for the report descriptor to be loaded by HID */
+ error = hid_parse(hdev);
+@@ -2659,6 +2660,7 @@ static void wacom_remove(struct hid_devi
+ cancel_work_sync(&wacom->battery_work);
+ cancel_work_sync(&wacom->remote_work);
+ cancel_work_sync(&wacom->mode_change_work);
++ del_timer_sync(&wacom->idleprox_timer);
+ if (hdev->bus == BUS_BLUETOOTH)
+ device_remove_file(&hdev->dev, &dev_attr_speed);
+
+--- a/drivers/hid/wacom_wac.c
++++ b/drivers/hid/wacom_wac.c
+@@ -15,6 +15,7 @@
+ #include "wacom_wac.h"
+ #include "wacom.h"
+ #include <linux/input/mt.h>
++#include <linux/jiffies.h>
+
+ /* resolution for penabled devices */
+ #define WACOM_PL_RES 20
+@@ -45,6 +46,43 @@ static int wacom_numbered_button_to_key(
+
+ static void wacom_update_led(struct wacom *wacom, int button_count, int mask,
+ int group);
++
++static void wacom_force_proxout(struct wacom_wac *wacom_wac)
++{
++ struct input_dev *input = wacom_wac->pen_input;
++
++ wacom_wac->shared->stylus_in_proximity = 0;
++
++ input_report_key(input, BTN_TOUCH, 0);
++ input_report_key(input, BTN_STYLUS, 0);
++ input_report_key(input, BTN_STYLUS2, 0);
++ input_report_key(input, BTN_STYLUS3, 0);
++ input_report_key(input, wacom_wac->tool[0], 0);
++ if (wacom_wac->serial[0]) {
++ input_report_abs(input, ABS_MISC, 0);
++ }
++ input_report_abs(input, ABS_PRESSURE, 0);
++
++ wacom_wac->tool[0] = 0;
++ wacom_wac->id[0] = 0;
++ wacom_wac->serial[0] = 0;
++
++ input_sync(input);
++}
++
++void wacom_idleprox_timeout(struct timer_list *list)
++{
++ struct wacom *wacom = from_timer(wacom, list, idleprox_timer);
++ struct wacom_wac *wacom_wac = &wacom->wacom_wac;
++
++ if (!wacom_wac->hid_data.sense_state) {
++ return;
++ }
++
++ hid_warn(wacom->hdev, "%s: tool appears to be hung in-prox. forcing it out.\n", __func__);
++ wacom_force_proxout(wacom_wac);
++}
++
+ /*
+ * Percent of battery capacity for Graphire.
+ * 8th value means AC online and show 100% capacity.
+@@ -2172,6 +2210,7 @@ static void wacom_wac_pen_event(struct h
+ value = field->logical_maximum - value;
+ break;
+ case HID_DG_INRANGE:
++ mod_timer(&wacom->idleprox_timer, jiffies + msecs_to_jiffies(100));
+ wacom_wac->hid_data.inrange_state = value;
+ if (!(features->quirks & WACOM_QUIRK_SENSE))
+ wacom_wac->hid_data.sense_state = value;