Merge branch 'next/dt2' into HEAD
[cascardo/linux.git] / drivers / hid / hid-wacom.c
index a92d116..2f60da9 100644 (file)
@@ -32,6 +32,8 @@
 #define PAD_DEVICE_ID  0x0F
 
 #define WAC_CMD_LED_CONTROL     0x20
+#define WAC_CMD_ICON_START_STOP     0x21
+#define WAC_CMD_ICON_TRANSFER       0x26
 
 struct wacom_data {
        __u16 tool;
@@ -68,6 +70,91 @@ static enum power_supply_property wacom_ac_props[] = {
        POWER_SUPPLY_PROP_SCOPE,
 };
 
+static void wacom_scramble(__u8 *image)
+{
+       __u16 mask;
+       __u16 s1;
+       __u16 s2;
+       __u16 r1 ;
+       __u16 r2 ;
+       __u16 r;
+       __u8 buf[256];
+       int i, w, x, y, z;
+
+       for (x = 0; x < 32; x++) {
+               for (y = 0; y < 8; y++)
+                       buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
+       }
+
+       /* Change 76543210 into GECA6420 as required by Intuos4 WL
+        *        HGFEDCBA      HFDB7531
+        */
+       for (x = 0; x < 4; x++) {
+               for (y = 0; y < 4; y++) {
+                       for (z = 0; z < 8; z++) {
+                               mask = 0x0001;
+                               r1 = 0;
+                               r2 = 0;
+                               i = (x << 6) + (y << 4) + z;
+                               s1 = buf[i];
+                               s2 = buf[i+8];
+                               for (w = 0; w < 8; w++) {
+                                       r1 |= (s1 & mask);
+                                       r2 |= (s2 & mask);
+                                       s1 <<= 1;
+                                       s2 <<= 1;
+                                       mask <<= 2;
+                               }
+                               r = r1 | (r2 << 1);
+                               i = (x << 6) + (y << 4) + (z << 1);
+                               image[i] = 0xFF & r;
+                               image[i+1] = (0xFF00 & r) >> 8;
+                       }
+               }
+       }
+}
+
+static void wacom_set_image(struct hid_device *hdev, const char *image,
+                                               __u8 icon_no)
+{
+       __u8 rep_data[68];
+       __u8 p[256];
+       int ret, i, j;
+
+       for (i = 0; i < 256; i++)
+               p[i] = image[i];
+
+       rep_data[0] = WAC_CMD_ICON_START_STOP;
+       rep_data[1] = 0;
+       ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+                               HID_FEATURE_REPORT);
+       if (ret < 0)
+               goto err;
+
+       rep_data[0] = WAC_CMD_ICON_TRANSFER;
+       rep_data[1] = icon_no & 0x07;
+
+       wacom_scramble(p);
+
+       for (i = 0; i < 4; i++) {
+               for (j = 0; j < 64; j++)
+                       rep_data[j + 3] = p[(i << 6) + j];
+
+               rep_data[2] = i;
+               ret = hdev->hid_output_raw_report(hdev, rep_data, 67,
+                                       HID_FEATURE_REPORT);
+       }
+
+       rep_data[0] = WAC_CMD_ICON_START_STOP;
+       rep_data[1] = 0;
+
+       ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+                               HID_FEATURE_REPORT);
+
+err:
+       return;
+}
+
 static void wacom_leds_set_brightness(struct led_classdev *led_dev,
                                                enum led_brightness value)
 {
@@ -90,7 +177,10 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
        if (buf) {
                buf[0] = WAC_CMD_LED_CONTROL;
                buf[1] = led;
-               buf[2] = value;
+               buf[2] = value >> 2;
+               buf[3] = value;
+               /* use fixed brightness for OLEDs */
+               buf[4] = 0x08;
                hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT);
                kfree(buf);
        }
@@ -316,6 +406,34 @@ static ssize_t wacom_store_speed(struct device *dev,
 static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
                wacom_show_speed, wacom_store_speed);
 
+#define WACOM_STORE(OLED_ID)                                           \
+static ssize_t wacom_oled##OLED_ID##_store(struct device *dev,         \
+                               struct device_attribute *attr,          \
+                               const char *buf, size_t count)          \
+{                                                                      \
+       struct hid_device *hdev = container_of(dev, struct hid_device,  \
+                               dev);                                   \
+                                                                       \
+       if (count != 256)                                               \
+               return -EINVAL;                                         \
+                                                                       \
+       wacom_set_image(hdev, buf, OLED_ID);                            \
+                                                                       \
+       return count;                                                   \
+}                                                                      \
+                                                                       \
+static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL,       \
+                               wacom_oled##OLED_ID##_store)
+
+WACOM_STORE(0);
+WACOM_STORE(1);
+WACOM_STORE(2);
+WACOM_STORE(3);
+WACOM_STORE(4);
+WACOM_STORE(5);
+WACOM_STORE(6);
+WACOM_STORE(7);
+
 static int wacom_gr_parse_report(struct hid_device *hdev,
                        struct wacom_data *wdata,
                        struct input_dev *input, unsigned char *data)
@@ -716,17 +834,33 @@ static int wacom_probe(struct hid_device *hdev,
                hid_warn(hdev,
                         "can't create sysfs speed attribute err: %d\n", ret);
 
+#define OLED_INIT(OLED_ID)                                             \
+       do {                                                            \
+               ret = device_create_file(&hdev->dev,                    \
+                               &dev_attr_oled##OLED_ID##_img);         \
+               if (ret)                                                \
+                       hid_warn(hdev,                                  \
+                        "can't create sysfs oled attribute, err: %d\n", ret);\
+       } while (0)
+
+OLED_INIT(0);
+OLED_INIT(1);
+OLED_INIT(2);
+OLED_INIT(3);
+OLED_INIT(4);
+OLED_INIT(5);
+OLED_INIT(6);
+OLED_INIT(7);
+
        wdata->features = 0;
        wacom_set_features(hdev, 1);
 
        if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
                sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
                ret = wacom_initialize_leds(hdev);
-               if (ret) {
+               if (ret)
                        hid_warn(hdev,
                                 "can't create led attribute, err: %d\n", ret);
-                       goto destroy_leds;
-               }
        }
 
        wdata->battery.properties = wacom_battery_props;
@@ -739,8 +873,8 @@ static int wacom_probe(struct hid_device *hdev,
 
        ret = power_supply_register(&hdev->dev, &wdata->battery);
        if (ret) {
-               hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
-                        ret);
+               hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
+                       ret);
                goto err_battery;
        }
 
@@ -755,8 +889,8 @@ static int wacom_probe(struct hid_device *hdev,
 
        ret = power_supply_register(&hdev->dev, &wdata->ac);
        if (ret) {
-               hid_warn(hdev,
-                        "can't create ac battery attribute, err: %d\n", ret);
+               hid_err(hdev,
+                       "can't create ac battery attribute, err: %d\n", ret);
                goto err_ac;
        }
 
@@ -766,10 +900,17 @@ static int wacom_probe(struct hid_device *hdev,
 err_ac:
        power_supply_unregister(&wdata->battery);
 err_battery:
+       wacom_destroy_leds(hdev);
+       device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled7_img);
        device_remove_file(&hdev->dev, &dev_attr_speed);
        hid_hw_stop(hdev);
-destroy_leds:
-       wacom_destroy_leds(hdev);
 err_free:
        kfree(wdata);
        return ret;
@@ -780,6 +921,14 @@ static void wacom_remove(struct hid_device *hdev)
        struct wacom_data *wdata = hid_get_drvdata(hdev);
 
        wacom_destroy_leds(hdev);
+       device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled7_img);
        device_remove_file(&hdev->dev, &dev_attr_speed);
        hid_hw_stop(hdev);