Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[pandora-kernel.git] / drivers / input / tablet / wacom_sys.c
index 79a0509..cad5602 100644 (file)
@@ -28,6 +28,7 @@
 #define HID_USAGE_Y_TILT               0x3e
 #define HID_USAGE_FINGER               0x22
 #define HID_USAGE_STYLUS               0x20
+#define HID_USAGE_CONTACTMAX           0x55
 #define HID_COLLECTION                 0xa1
 #define HID_COLLECTION_LOGICAL         0x02
 #define HID_COLLECTION_END             0xc0
@@ -204,6 +205,27 @@ static int wacom_parse_logical_collection(unsigned char *report,
        return length;
 }
 
+static void wacom_retrieve_report_data(struct usb_interface *intf,
+                                      struct wacom_features *features)
+{
+       int result = 0;
+       unsigned char *rep_data;
+
+       rep_data = kmalloc(2, GFP_KERNEL);
+       if (rep_data) {
+
+               rep_data[0] = 12;
+               result = wacom_get_report(intf, WAC_HID_FEATURE_REPORT,
+                                         rep_data[0], &rep_data, 2,
+                                         WAC_MSG_RETRIES);
+
+               if (result >= 0 && rep_data[1] > 2)
+                       features->touch_max = rep_data[1];
+
+               kfree(rep_data);
+       }
+}
+
 /*
  * Interface Descriptor of wacom devices can be incomplete and
  * inconsistent so wacom_features table is used to store stylus
@@ -236,6 +258,9 @@ static int wacom_parse_logical_collection(unsigned char *report,
  * 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical
  * Collection. Instead they define a Logical Collection with a single
  * Logical Maximum for both X and Y.
+ *
+ * Intuos5 touch interface does not contain useful data. We deal with
+ * this after returning from this function.
  */
 static int wacom_parse_hid(struct usb_interface *intf,
                           struct hid_descriptor *hid_desc,
@@ -295,6 +320,10 @@ static int wacom_parse_hid(struct usb_interface *intf,
                                                        /* need to reset back */
                                                        features->pktlen = WACOM_PKGLEN_TPC2FG;
                                                }
+
+                                               if (features->type == MTSCREEN)
+                                                       features->pktlen = WACOM_PKGLEN_MTOUCH;
+
                                                if (features->type == BAMBOO_PT) {
                                                        /* need to reset back */
                                                        features->pktlen = WACOM_PKGLEN_BBTOUCH;
@@ -327,18 +356,15 @@ static int wacom_parse_hid(struct usb_interface *intf,
                        case HID_USAGE_Y:
                                if (usage == WCM_DESKTOP) {
                                        if (finger) {
-                                               features->device_type = BTN_TOOL_FINGER;
-                                               if (features->type == TABLETPC2FG) {
-                                                       /* need to reset back */
-                                                       features->pktlen = WACOM_PKGLEN_TPC2FG;
+                                               int type = features->type;
+
+                                               if (type == TABLETPC2FG || type == MTSCREEN) {
                                                        features->y_max =
                                                                get_unaligned_le16(&report[i + 3]);
                                                        features->y_phy =
                                                                get_unaligned_le16(&report[i + 6]);
                                                        i += 7;
-                                               } else if (features->type == BAMBOO_PT) {
-                                                       /* need to reset back */
-                                                       features->pktlen = WACOM_PKGLEN_BBTOUCH;
+                                               } else if (type == BAMBOO_PT) {
                                                        features->y_phy =
                                                                get_unaligned_le16(&report[i + 3]);
                                                        features->y_max =
@@ -352,10 +378,6 @@ static int wacom_parse_hid(struct usb_interface *intf,
                                                        i += 4;
                                                }
                                        } else if (pen) {
-                                               /* penabled only accepts exact bytes of data */
-                                               if (features->type == TABLETPC2FG)
-                                                       features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-                                               features->device_type = BTN_TOOL_PEN;
                                                features->y_max =
                                                        get_unaligned_le16(&report[i + 3]);
                                                i += 4;
@@ -377,6 +399,11 @@ static int wacom_parse_hid(struct usb_interface *intf,
                                pen = 1;
                                i++;
                                break;
+
+                       case HID_USAGE_CONTACTMAX:
+                               wacom_retrieve_report_data(intf, features);
+                               i++;
+                               break;
                        }
                        break;
 
@@ -413,22 +440,29 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
        if (!rep_data)
                return error;
 
-       /* ask to report tablet data if it is MT Tablet PC or
-        * not a Tablet PC */
-       if (features->type == TABLETPC2FG) {
-               do {
-                       rep_data[0] = 3;
-                       rep_data[1] = 4;
-                       rep_data[2] = 0;
-                       rep_data[3] = 0;
-                       report_id = 3;
-                       error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
-                                                report_id, rep_data, 4, 1);
-                       if (error >= 0)
-                               error = wacom_get_report(intf,
-                                               WAC_HID_FEATURE_REPORT,
-                                               report_id, rep_data, 4, 1);
-               } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
+       /* ask to report Wacom data */
+       if (features->device_type == BTN_TOOL_FINGER) {
+               /* if it is an MT Tablet PC touch */
+               if (features->type == TABLETPC2FG ||
+                   features->type == MTSCREEN) {
+                       do {
+                               rep_data[0] = 3;
+                               rep_data[1] = 4;
+                               rep_data[2] = 0;
+                               rep_data[3] = 0;
+                               report_id = 3;
+                               error = wacom_set_report(intf,
+                                                        WAC_HID_FEATURE_REPORT,
+                                                        report_id,
+                                                        rep_data, 4, 1);
+                               if (error >= 0)
+                                       error = wacom_get_report(intf,
+                                                       WAC_HID_FEATURE_REPORT,
+                                                       report_id,
+                                                       rep_data, 4, 1);
+                       } while ((error < 0 || rep_data[1] != 4) &&
+                                limit++ < WAC_MSG_RETRIES);
+               }
        } else if (features->type != TABLETPC &&
                   features->type != WIRELESS &&
                   features->device_type == BTN_TOOL_PEN) {
@@ -450,7 +484,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
 }
 
 static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
-               struct wacom_features *features)
+                                        struct wacom_features *features)
 {
        int error = 0;
        struct usb_host_interface *interface = intf->cur_altsetting;
@@ -478,16 +512,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
                }
        }
 
-       /* only Tablet PCs and Bamboo P&T need to retrieve the info */
-       if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
-           (features->type != BAMBOO_PT))
+       /* only devices that support touch need to retrieve the info */
+       if (features->type != TABLETPC &&
+           features->type != TABLETPC2FG &&
+           features->type != BAMBOO_PT &&
+           features->type != MTSCREEN) {
                goto out;
+       }
 
-       if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
-               if (usb_get_extra_descriptor(&interface->endpoint[0],
-                               HID_DEVICET_REPORT, &hid_desc)) {
-                       printk("wacom: can not retrieve extra class descriptor\n");
-                       error = 1;
+       error = usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc);
+       if (error) {
+               error = usb_get_extra_descriptor(&interface->endpoint[0],
+                                                HID_DEVICET_REPORT, &hid_desc);
+               if (error) {
+                       dev_err(&intf->dev,
+                               "can not retrieve extra class descriptor\n");
                        goto out;
                }
        }
@@ -577,23 +616,39 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom)
 static int wacom_led_control(struct wacom *wacom)
 {
        unsigned char *buf;
-       int retval, led = 0;
+       int retval;
 
        buf = kzalloc(9, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       if (wacom->wacom_wac.features.type == WACOM_21UX2 ||
-           wacom->wacom_wac.features.type == WACOM_24HD)
-               led = (wacom->led.select[1] << 4) | 0x40;
-
-       led |=  wacom->led.select[0] | 0x4;
-
-       buf[0] = WAC_CMD_LED_CONTROL;
-       buf[1] = led;
-       buf[2] = wacom->led.llv;
-       buf[3] = wacom->led.hlv;
-       buf[4] = wacom->led.img_lum;
+       if (wacom->wacom_wac.features.type >= INTUOS5S &&
+           wacom->wacom_wac.features.type <= INTUOS5L) {
+               /*
+                * Touch Ring and crop mark LED luminance may take on
+                * one of four values:
+                *    0 = Low; 1 = Medium; 2 = High; 3 = Off
+                */
+               int ring_led = wacom->led.select[0] & 0x03;
+               int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
+               int crop_lum = 0;
+
+               buf[0] = WAC_CMD_LED_CONTROL;
+               buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
+       }
+       else {
+               int led = wacom->led.select[0] | 0x4;
+
+               if (wacom->wacom_wac.features.type == WACOM_21UX2 ||
+                   wacom->wacom_wac.features.type == WACOM_24HD)
+                       led |= (wacom->led.select[1] << 4) | 0x40;
+
+               buf[0] = WAC_CMD_LED_CONTROL;
+               buf[1] = led;
+               buf[2] = wacom->led.llv;
+               buf[3] = wacom->led.hlv;
+               buf[4] = wacom->led.img_lum;
+       }
 
        retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL,
                                  buf, 9, WAC_CMD_RETRIES);
@@ -786,6 +841,17 @@ static struct attribute_group intuos4_led_attr_group = {
        .attrs = intuos4_led_attrs,
 };
 
+static struct attribute *intuos5_led_attrs[] = {
+       &dev_attr_status0_luminance.attr,
+       &dev_attr_status_led0_select.attr,
+       NULL
+};
+
+static struct attribute_group intuos5_led_attr_group = {
+       .name = "wacom_led",
+       .attrs = intuos5_led_attrs,
+};
+
 static int wacom_initialize_leds(struct wacom *wacom)
 {
        int error;
@@ -815,6 +881,19 @@ static int wacom_initialize_leds(struct wacom *wacom)
                                           &cintiq_led_attr_group);
                break;
 
+       case INTUOS5S:
+       case INTUOS5:
+       case INTUOS5L:
+               wacom->led.select[0] = 0;
+               wacom->led.select[1] = 0;
+               wacom->led.llv = 32;
+               wacom->led.hlv = 0;
+               wacom->led.img_lum = 0;
+
+               error = sysfs_create_group(&wacom->intf->dev.kobj,
+                                          &intuos5_led_attr_group);
+               break;
+
        default:
                return 0;
        }
@@ -843,6 +922,13 @@ static void wacom_destroy_leds(struct wacom *wacom)
                sysfs_remove_group(&wacom->intf->dev.kobj,
                                   &cintiq_led_attr_group);
                break;
+
+       case INTUOS5S:
+       case INTUOS5:
+       case INTUOS5L:
+               sysfs_remove_group(&wacom->intf->dev.kobj,
+                                  &intuos5_led_attr_group);
+               break;
        }
 }
 
@@ -904,8 +990,10 @@ static int wacom_register_input(struct wacom *wacom)
        int error;
 
        input_dev = input_allocate_device();
-       if (!input_dev)
-               return -ENOMEM;
+       if (!input_dev) {
+               error = -ENOMEM;
+               goto fail1;
+       }
 
        input_dev->name = wacom_wac->name;
        input_dev->dev.parent = &intf->dev;
@@ -915,14 +1003,20 @@ static int wacom_register_input(struct wacom *wacom)
        input_set_drvdata(input_dev, wacom);
 
        wacom_wac->input = input_dev;
-       wacom_setup_input_capabilities(input_dev, wacom_wac);
+       error = wacom_setup_input_capabilities(input_dev, wacom_wac);
+       if (error)
+               goto fail1;
 
        error = input_register_device(input_dev);
-       if (error) {
-               input_free_device(input_dev);
-               wacom_wac->input = NULL;
-       }
+       if (error)
+               goto fail2;
 
+       return 0;
+
+fail2:
+       input_free_device(input_dev);
+       wacom_wac->input = NULL;
+fail1:
        return error;
 }
 
@@ -941,22 +1035,22 @@ static void wacom_wireless_work(struct work_struct *work)
        wacom = usb_get_intfdata(usbdev->config->interface[1]);
        if (wacom->wacom_wac.input)
                input_unregister_device(wacom->wacom_wac.input);
-       wacom->wacom_wac.input = 0;
+       wacom->wacom_wac.input = NULL;
 
        /* Touch interface */
        wacom = usb_get_intfdata(usbdev->config->interface[2]);
        if (wacom->wacom_wac.input)
                input_unregister_device(wacom->wacom_wac.input);
-       wacom->wacom_wac.input = 0;
+       wacom->wacom_wac.input = NULL;
 
        if (wacom_wac->pid == 0) {
-               printk(KERN_INFO "wacom: wireless tablet disconnected\n");
+               dev_info(&wacom->intf->dev, "wireless tablet disconnected\n");
        } else {
                const struct usb_device_id *id = wacom_ids;
 
-               printk(KERN_INFO
-                      "wacom: wireless tablet connected with PID %x\n",
-                      wacom_wac->pid);
+               dev_info(&wacom->intf->dev,
+                        "wireless tablet connected with PID %x\n",
+                        wacom_wac->pid);
 
                while (id->match_flags) {
                        if (id->idVendor == USB_VENDOR_ID_WACOM &&
@@ -966,8 +1060,8 @@ static void wacom_wireless_work(struct work_struct *work)
                }
 
                if (!id->match_flags) {
-                       printk(KERN_INFO
-                              "wacom: ignorning unknown PID.\n");
+                       dev_info(&wacom->intf->dev,
+                                "ignoring unknown PID.\n");
                        return;
                }
 
@@ -1038,11 +1132,33 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 
        endpoint = &intf->cur_altsetting->endpoint[0].desc;
 
-       /* Retrieve the physical and logical size for OEM devices */
+       /* Retrieve the physical and logical size for touch devices */
        error = wacom_retrieve_hid_descriptor(intf, features);
        if (error)
                goto fail3;
 
+       /*
+        * Intuos5 has no useful data about its touch interface in its
+        * HID descriptor. If this is the touch interface (wMaxPacketSize
+        * of WACOM_PKGLEN_BBTOUCH3), override the table values.
+        */
+       if (features->type >= INTUOS5S && features->type <= INTUOS5L) {
+               if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) {
+                       features->device_type = BTN_TOOL_FINGER;
+                       features->pktlen = WACOM_PKGLEN_BBTOUCH3;
+
+                       features->x_phy =
+                               (features->x_max * 100) / features->x_resolution;
+                       features->y_phy =
+                               (features->y_max * 100) / features->y_resolution;
+
+                       features->x_max = 4096;
+                       features->y_max = 4096;
+               } else {
+                       features->device_type = BTN_TOOL_PEN;
+               }
+       }
+
        wacom_setup_device_quirks(features);
 
        strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));