HID: multitouch: Add quirks for flipped axes
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> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
d264dd3bbb
commit
a2f416bf06
|
@ -71,6 +71,7 @@ MODULE_LICENSE("GPL");
|
||||||
#define MT_QUIRK_SEPARATE_APP_REPORT BIT(19)
|
#define MT_QUIRK_SEPARATE_APP_REPORT BIT(19)
|
||||||
#define MT_QUIRK_FORCE_MULTI_INPUT BIT(20)
|
#define MT_QUIRK_FORCE_MULTI_INPUT BIT(20)
|
||||||
#define MT_QUIRK_DISABLE_WAKEUP BIT(21)
|
#define MT_QUIRK_DISABLE_WAKEUP BIT(21)
|
||||||
|
#define MT_QUIRK_ORIENTATION_INVERT BIT(22)
|
||||||
|
|
||||||
#define MT_INPUTMODE_TOUCHSCREEN 0x02
|
#define MT_INPUTMODE_TOUCHSCREEN 0x02
|
||||||
#define MT_INPUTMODE_TOUCHPAD 0x03
|
#define MT_INPUTMODE_TOUCHPAD 0x03
|
||||||
|
@ -1009,6 +1010,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
|
||||||
struct mt_usages *slot)
|
struct mt_usages *slot)
|
||||||
{
|
{
|
||||||
struct input_mt *mt = input->mt;
|
struct input_mt *mt = input->mt;
|
||||||
|
struct hid_device *hdev = td->hdev;
|
||||||
__s32 quirks = app->quirks;
|
__s32 quirks = app->quirks;
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
bool confidence_state = true;
|
bool confidence_state = true;
|
||||||
|
@ -1086,6 +1088,10 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
|
||||||
int orientation = wide;
|
int orientation = wide;
|
||||||
int max_azimuth;
|
int max_azimuth;
|
||||||
int azimuth;
|
int azimuth;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int cx;
|
||||||
|
int cy;
|
||||||
|
|
||||||
if (slot->a != DEFAULT_ZERO) {
|
if (slot->a != DEFAULT_ZERO) {
|
||||||
/*
|
/*
|
||||||
|
@ -1104,6 +1110,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
|
||||||
if (azimuth > max_azimuth * 2)
|
if (azimuth > max_azimuth * 2)
|
||||||
azimuth -= max_azimuth * 4;
|
azimuth -= max_azimuth * 4;
|
||||||
orientation = -azimuth;
|
orientation = -azimuth;
|
||||||
|
if (quirks & MT_QUIRK_ORIENTATION_INVERT)
|
||||||
|
orientation = -orientation;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
|
if (quirks & MT_QUIRK_TOUCH_SIZE_SCALING) {
|
||||||
|
@ -1115,10 +1124,23 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
|
||||||
minor = minor >> 1;
|
minor = minor >> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
input_event(input, EV_ABS, ABS_MT_POSITION_X, *slot->x);
|
x = hdev->quirks & HID_QUIRK_X_INVERT ?
|
||||||
input_event(input, EV_ABS, ABS_MT_POSITION_Y, *slot->y);
|
input_abs_get_max(input, ABS_MT_POSITION_X) - *slot->x :
|
||||||
input_event(input, EV_ABS, ABS_MT_TOOL_X, *slot->cx);
|
*slot->x;
|
||||||
input_event(input, EV_ABS, ABS_MT_TOOL_Y, *slot->cy);
|
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_DISTANCE, !*slot->tip_state);
|
||||||
input_event(input, EV_ABS, ABS_MT_ORIENTATION, orientation);
|
input_event(input, EV_ABS, ABS_MT_ORIENTATION, orientation);
|
||||||
input_event(input, EV_ABS, ABS_MT_PRESSURE, *slot->p);
|
input_event(input, EV_ABS, ABS_MT_PRESSURE, *slot->p);
|
||||||
|
@ -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)
|
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
|
||||||
td->serial_maybe = true;
|
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
|
/* This allows the driver to correctly support devices
|
||||||
* that emit events over several HID messages.
|
* that emit events over several HID messages.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/input/elan-i2c-ids.h>
|
#include <linux/input/elan-i2c-ids.h>
|
||||||
|
|
||||||
#include "hid-ids.h"
|
#include "hid-ids.h"
|
||||||
|
#include "i2c-hid/i2c-hid.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alphabetically sorted by vendor then product.
|
* Alphabetically sorted by vendor then product.
|
||||||
|
@ -1298,6 +1299,11 @@ unsigned long hid_lookup_quirk(const struct hid_device *hdev)
|
||||||
quirks = hid_gets_squirk(hdev);
|
quirks = hid_gets_squirk(hdev);
|
||||||
mutex_unlock(&dquirks_lock);
|
mutex_unlock(&dquirks_lock);
|
||||||
|
|
||||||
|
/* Get quirks specific to I2C devices */
|
||||||
|
if (IS_ENABLED(CONFIG_I2C_DMI_CORE) && IS_ENABLED(CONFIG_DMI) &&
|
||||||
|
hdev->bus == BUS_I2C)
|
||||||
|
quirks |= i2c_hid_get_dmi_quirks(hdev->vendor, hdev->product);
|
||||||
|
|
||||||
return quirks;
|
return quirks;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hid_lookup_quirk);
|
EXPORT_SYMBOL_GPL(hid_lookup_quirk);
|
||||||
|
|
|
@ -10,8 +10,10 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/hid.h>
|
||||||
|
|
||||||
#include "i2c-hid.h"
|
#include "i2c-hid.h"
|
||||||
|
#include "../hid-ids.h"
|
||||||
|
|
||||||
|
|
||||||
struct i2c_hid_desc_override {
|
struct i2c_hid_desc_override {
|
||||||
|
@ -416,6 +418,28 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
|
||||||
{ } /* Terminate list */
|
{ } /* 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)
|
struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
|
||||||
{
|
{
|
||||||
|
@ -450,3 +474,22 @@ char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
||||||
*size = override->hid_report_desc_size;
|
*size = override->hid_report_desc_size;
|
||||||
return override->hid_report_desc;
|
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);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name);
|
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,
|
char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
||||||
unsigned int *size);
|
unsigned int *size);
|
||||||
|
u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product);
|
||||||
#else
|
#else
|
||||||
static inline struct i2c_hid_desc
|
static inline struct i2c_hid_desc
|
||||||
*i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
|
*i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
|
||||||
|
@ -16,6 +17,8 @@ static inline struct i2c_hid_desc
|
||||||
static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
|
||||||
unsigned int *size)
|
unsigned int *size)
|
||||||
{ return NULL; }
|
{ return NULL; }
|
||||||
|
static inline u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product)
|
||||||
|
{ return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue