forked from mirrors/linux
		
	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
					
				
					 4 changed files with 87 additions and 4 deletions
				
			
		| 
						 | 
					@ -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 a new issue