Skip to content

Commit

Permalink
HID: multitouch: Add quirks for flipped axes
Browse files Browse the repository at this point in the history
Certain touchscreen devices, such as the ELAN9034, are oriented
incorrectly and report touches on opposite points on the X and Y axes.
For example, a 100x200 screen touched at (10,20) would report (90, 180)
and vice versa.

This is fixed by adding device quirks to transform the touch points
into the correct spaces, from X -> MAX(X) - X, and Y -> MAX(Y) - Y.

Signed-off-by: Allen Ballway <ballway@chromium.org>
  • Loading branch information
Allen Ballway authored and intel-lab-lkp committed Dec 14, 2022
1 parent d9beee4 commit 3a75289
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 4 deletions.
39 changes: 35 additions & 4 deletions drivers/hid/hid-multitouch.c
Expand Up @@ -71,6 +71,7 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_SEPARATE_APP_REPORT BIT(19)
#define MT_QUIRK_FORCE_MULTI_INPUT BIT(20)
#define MT_QUIRK_DISABLE_WAKEUP BIT(21)
#define MT_QUIRK_ORIENTATION_INVERT BIT(22)

#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
Expand Down Expand Up @@ -1009,6 +1010,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
struct mt_usages *slot)
{
struct input_mt *mt = input->mt;
struct hid_device *hdev = td->hdev;
__s32 quirks = app->quirks;
bool valid = true;
bool confidence_state = true;
Expand Down Expand Up @@ -1086,6 +1088,10 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
int orientation = wide;
int max_azimuth;
int azimuth;
int x;
int y;
int cx;
int cy;

if (slot->a != DEFAULT_ZERO) {
/*
Expand All @@ -1104,6 +1110,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
if (azimuth > max_azimuth * 2)
azimuth -= max_azimuth * 4;
orientation = -azimuth;
if (quirks & MT_QUIRK_ORIENTATION_INVERT)
orientation = -orientation;

}

if (quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
Expand All @@ -1115,10 +1124,23 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
minor = minor >> 1;
}

input_event(input, EV_ABS, ABS_MT_POSITION_X, *slot->x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, *slot->y);
input_event(input, EV_ABS, ABS_MT_TOOL_X, *slot->cx);
input_event(input, EV_ABS, ABS_MT_TOOL_Y, *slot->cy);
x = hdev->quirks & HID_QUIRK_X_INVERT ?
input_abs_get_max(input, ABS_MT_POSITION_X) - *slot->x :
*slot->x;
y = hdev->quirks & HID_QUIRK_Y_INVERT ?
input_abs_get_max(input, ABS_MT_POSITION_Y) - *slot->y :
*slot->y;
cx = hdev->quirks & HID_QUIRK_X_INVERT ?
input_abs_get_max(input, ABS_MT_POSITION_X) - *slot->cx :
*slot->cx;
cy = hdev->quirks & HID_QUIRK_Y_INVERT ?
input_abs_get_max(input, ABS_MT_POSITION_Y) - *slot->cy :
*slot->cy;

input_event(input, EV_ABS, ABS_MT_POSITION_X, x);
input_event(input, EV_ABS, ABS_MT_POSITION_Y, y);
input_event(input, EV_ABS, ABS_MT_TOOL_X, cx);
input_event(input, EV_ABS, ABS_MT_TOOL_Y, cy);
input_event(input, EV_ABS, ABS_MT_DISTANCE, !*slot->tip_state);
input_event(input, EV_ABS, ABS_MT_ORIENTATION, orientation);
input_event(input, EV_ABS, ABS_MT_PRESSURE, *slot->p);
Expand Down Expand Up @@ -1735,6 +1757,15 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
td->serial_maybe = true;


/* Orientation is inverted if the X or Y axes are
* flipped, but normalized if both are inverted.
*/
if (hdev->quirks & (HID_QUIRK_X_INVERT | HID_QUIRK_Y_INVERT) &&
!((hdev->quirks & HID_QUIRK_X_INVERT)
&& (hdev->quirks & HID_QUIRK_Y_INVERT)))
td->mtclass.quirks = MT_QUIRK_ORIENTATION_INVERT;

/* This allows the driver to correctly support devices
* that emit events over several HID messages.
*/
Expand Down
5 changes: 5 additions & 0 deletions drivers/hid/hid-quirks.c
Expand Up @@ -19,6 +19,7 @@
#include <linux/input/elan-i2c-ids.h>

#include "hid-ids.h"
#include "i2c-hid/i2c-hid.h"

/*
* Alphabetically sorted by vendor then product.
Expand Down Expand Up @@ -1298,6 +1299,10 @@ unsigned long hid_lookup_quirk(const struct hid_device *hdev)
quirks = hid_gets_squirk(hdev);
mutex_unlock(&dquirks_lock);

/* Get quirks specific to I2C devices */
if (IS_ENABLED(CONFIG_DMI) && hdev->bus == BUS_I2C)
quirks |= i2c_hid_get_dmi_quirks(hdev->vendor, hdev->product);

return quirks;
}
EXPORT_SYMBOL_GPL(hid_lookup_quirk);
43 changes: 43 additions & 0 deletions drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
Expand Up @@ -10,8 +10,10 @@
#include <linux/types.h>
#include <linux/dmi.h>
#include <linux/mod_devicetable.h>
#include <linux/hid.h>

#include "i2c-hid.h"
#include "../hid-ids.h"


struct i2c_hid_desc_override {
Expand Down Expand Up @@ -416,6 +418,28 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
{ } /* Terminate list */
};

static const struct hid_device_id i2c_hid_elan_flipped_quirks = {
HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_ELAN, 0x2dcd),
HID_QUIRK_X_INVERT | HID_QUIRK_Y_INVERT
};

/*
* This list contains devices which have specific issues based on the system
* they're on and not just the device itself. The driver_data will have a
* specific hid device to match against.
*/
static const struct dmi_system_id i2c_hid_dmi_quirk_table[] = {
{
.ident = "DynaBook K50/FR",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dynabook Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "dynabook K50/FR"),
},
.driver_data = (void *)&i2c_hid_elan_flipped_quirks,
},
{ } /* Terminate list */
};


struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
{
Expand Down Expand Up @@ -450,3 +474,22 @@ char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
*size = override->hid_report_desc_size;
return override->hid_report_desc;
}

u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product)
{
u32 quirks = 0;
const struct dmi_system_id *system_id =
dmi_first_match(i2c_hid_dmi_quirk_table);

if (system_id) {
const struct hid_device_id *device_id =
(struct hid_device_id *)(system_id->driver_data);

if (device_id && device_id->vendor == vendor &&
device_id->product == product)
quirks = device_id->driver_data;
}

return quirks;
}
EXPORT_SYMBOL_GPL(i2c_hid_get_dmi_quirks);
3 changes: 3 additions & 0 deletions drivers/hid/i2c-hid/i2c-hid.h
Expand Up @@ -9,13 +9,16 @@
struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name);
char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
unsigned int *size);
u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product);
#else
static inline struct i2c_hid_desc
*i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
{ return NULL; }
static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
unsigned int *size)
{ return NULL; }
static inline u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product)
{ return 0; }
#endif

/**
Expand Down

0 comments on commit 3a75289

Please sign in to comment.