]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/hid/hid-multitouch.c
HID: hid-multitouch: add support for Ilitek dual-touch panel
[karo-tx-linux.git] / drivers / hid / hid-multitouch.c
index e6e1ea2514906ab33ac55b2d5e7752fbd627b5a5..b21251b43a351ca256b4a36267936bf1783f92af 100644 (file)
  *  Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se>
  *  Copyright (c) 2010 Canonical, Ltd.
  *
+ *  This code is partly based on hid-3m-pct.c:
+ *
+ *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
+ *  Copyright (c) 2010      Henrik Rydberg <rydberg@euromail.se>
+ *  Copyright (c) 2010      Canonical, Ltd.
+ *
  */
 
 /*
@@ -44,6 +50,7 @@ MODULE_LICENSE("GPL");
 #define MT_QUIRK_VALID_IS_INRANGE      (1 << 4)
 #define MT_QUIRK_VALID_IS_CONFIDENCE   (1 << 5)
 #define MT_QUIRK_EGALAX_XYZ_FIXUP      (1 << 6)
+#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE   (1 << 7)
 
 struct mt_slot {
        __s32 x, y, p, w, h;
@@ -69,6 +76,8 @@ struct mt_class {
        __s32 name;     /* MT_CLS */
        __s32 quirks;
        __s32 sn_move;  /* Signal/noise ratio for move events */
+       __s32 sn_width; /* Signal/noise ratio for width events */
+       __s32 sn_height;        /* Signal/noise ratio for height events */
        __s32 sn_pressure;      /* Signal/noise ratio for pressure events */
        __u8 maxcontacts;
 };
@@ -80,6 +89,9 @@ struct mt_class {
 #define MT_CLS_CYPRESS                         4
 #define MT_CLS_EGALAX                          5
 #define MT_CLS_STANTUM                         6
+#define MT_CLS_3M                              7
+#define MT_CLS_CONFIDENCE                      8
+#define MT_CLS_CONFIDENCE_MINUS_ONE            9
 
 #define MT_DEFAULT_MAXCONTACT  10
 
@@ -130,7 +142,9 @@ struct mt_class mt_classes[] = {
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
                        MT_QUIRK_CYPRESS,
                .maxcontacts = 10 },
-
+       { .name = MT_CLS_CONFIDENCE_MINUS_ONE,
+               .quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
+                       MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE },
        { .name = MT_CLS_EGALAX,
                .quirks =  MT_QUIRK_SLOT_IS_CONTACTID |
                        MT_QUIRK_VALID_IS_INRANGE |
@@ -141,6 +155,14 @@ struct mt_class mt_classes[] = {
        },
        { .name = MT_CLS_STANTUM,
                .quirks = MT_QUIRK_VALID_IS_CONFIDENCE },
+       { .name = MT_CLS_3M,
+               .quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
+                       MT_QUIRK_SLOT_IS_CONTACTID,
+               .sn_move = 2048,
+               .sn_width = 128,
+               .sn_height = 128 },
+       { .name = MT_CLS_CONFIDENCE,
+               .quirks = MT_QUIRK_VALID_IS_CONFIDENCE },
 
        { }
 };
@@ -195,6 +217,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        /* touchscreen emulation */
                        set_abs(hi->input, ABS_X, field, cls->sn_move);
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_GD_Y:
                        if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
@@ -206,6 +229,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        /* touchscreen emulation */
                        set_abs(hi->input, ABS_Y, field, cls->sn_move);
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                }
                return 0;
@@ -214,31 +238,40 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                switch (usage->hid) {
                case HID_DG_INRANGE:
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONFIDENCE:
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_TIPSWITCH:
                        hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
                        input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONTACTID:
                        input_mt_init_slots(hi->input, td->maxcontacts);
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_WIDTH:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_TOUCH_MAJOR);
+                       set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
+                               cls->sn_width);
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_HEIGHT:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_TOUCH_MINOR);
-                       field->logical_maximum = 1;
-                       field->logical_minimum = 0;
-                       set_abs(hi->input, ABS_MT_ORIENTATION, field, 0);
+                       set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
+                               cls->sn_height);
+                       input_set_abs_params(hi->input,
+                                       ABS_MT_ORIENTATION, 0, 1, 0, 0);
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_TIPPRESSURE:
                        if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
@@ -251,13 +284,15 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        set_abs(hi->input, ABS_PRESSURE, field,
                                cls->sn_pressure);
                        td->last_slot_field = usage->hid;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONTACTCOUNT:
-                       td->last_field_index = field->report->maxfield - 1;
+                       td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONTACTMAX:
                        /* we don't set td->last_slot_field as contactcount and
                         * contact max are global to the report */
+                       td->last_field_index = field->index;
                        return -1;
                }
                /* let hid-input decide for the others */
@@ -294,6 +329,9 @@ static int mt_compute_slot(struct mt_device *td)
        if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER)
                return td->num_received;
 
+       if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE)
+               return td->curdata.contactid - 1;
+
        return find_slot_from_contactid(td);
 }
 
@@ -333,11 +371,18 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input)
                input_mt_report_slot_state(input, MT_TOOL_FINGER,
                        s->touch_state);
                if (s->touch_state) {
+                       /* this finger is on the screen */
+                       int wide = (s->w > s->h);
+                       /* divided by two to match visual scale of touch */
+                       int major = max(s->w, s->h) >> 1;
+                       int minor = min(s->w, s->h) >> 1;
+
                        input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x);
                        input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y);
+                       input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
                        input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
-                       input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->w);
-                       input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, s->h);
+                       input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+                       input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
                }
                s->seen_in_this_frame = false;
 
@@ -405,8 +450,6 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
 
                if (usage->hid == td->last_slot_field) {
                        mt_complete_slot(td);
-                       if (!td->last_field_index)
-                               mt_emit_event(td, field->hidinput->input);
                }
 
                if (field->index == td->last_field_index
@@ -514,6 +557,14 @@ static void mt_remove(struct hid_device *hdev)
 
 static const struct hid_device_id mt_devices[] = {
 
+       /* 3M panels */
+       { .driver_data = MT_CLS_3M,
+               HID_USB_DEVICE(USB_VENDOR_ID_3M,
+                       USB_DEVICE_ID_3M1968) },
+       { .driver_data = MT_CLS_3M,
+               HID_USB_DEVICE(USB_VENDOR_ID_3M,
+                       USB_DEVICE_ID_3M2256) },
+
        /* Cando panels */
        { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
                HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
@@ -538,11 +589,32 @@ static const struct hid_device_id mt_devices[] = {
                HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
                        USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
 
+       /* Ilitek dual touch panel */
+       {  .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_ILITEK,
+                       USB_DEVICE_ID_ILITEK_MULTITOUCH) },
+
        /* IRTOUCH panels */
        { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
                HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS,
                        USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
 
+       /* MosArt panels */
+       { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
+               HID_USB_DEVICE(USB_VENDOR_ID_ASUS,
+                       USB_DEVICE_ID_ASUS_T91MT)},
+       { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
+               HID_USB_DEVICE(USB_VENDOR_ID_ASUS,
+                       USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) },
+       { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,
+               HID_USB_DEVICE(USB_VENDOR_ID_TURBOX,
+                       USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
+
+       /* PenMount panels */
+       { .driver_data = MT_CLS_CONFIDENCE,
+               HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT,
+                       USB_DEVICE_ID_PENMOUNT_PCI) },
+
        /* PixCir-based panels */
        { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
                HID_USB_DEVICE(USB_VENDOR_ID_HANVON,