summaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2016-07-13 18:06:10 +0200
committerJiri Kosina <jkosina@suse.cz>2016-08-05 13:39:19 +0200
commit97f5541fc0c7ed107103e6f87a6522f5327ab4b0 (patch)
treefa1e72787ac36f7fb130a20d12cad0227bb21717 /drivers/hid
parent9f1015d45f62d3b1c6719a1ccffaded89b157e10 (diff)
HID: wacom: leds: use the ledclass instead of custom made sysfs files
The now obsolete sysfs files for LEDs and EKRemote are kept for backward compatibility. Both the EKR (read-only) and the regular Cintiqs and Intuos are now sharing the same led API. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Acked-by: Ping Cheng <pingc@wacom.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/wacom.h19
-rw-r--r--drivers/hid/wacom_sys.c195
2 files changed, 204 insertions, 10 deletions
diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
index 768d69602e7a..0c498c46de5f 100644
--- a/drivers/hid/wacom.h
+++ b/drivers/hid/wacom.h
@@ -91,6 +91,7 @@
#include <linux/mod_devicetable.h>
#include <linux/hid.h>
#include <linux/kfifo.h>
+#include <linux/leds.h>
#include <linux/usb/input.h>
#include <linux/power_supply.h>
#include <asm/unaligned.h>
@@ -112,8 +113,23 @@ enum wacom_worker {
WACOM_WORKER_REMOTE,
};
+struct wacom;
+
+struct wacom_led {
+ struct led_classdev cdev;
+ struct led_trigger trigger;
+ struct wacom *wacom;
+ unsigned int group;
+ unsigned int id;
+ u8 llv;
+ u8 hlv;
+ bool held;
+};
+
struct wacom_group_leds {
u8 select; /* status led selector (0..3) */
+ struct wacom_led *leds;
+ unsigned int count;
};
struct wacom_battery {
@@ -154,9 +170,12 @@ struct wacom {
struct wacom_remote *remote;
struct wacom_leds {
struct wacom_group_leds *groups;
+ unsigned int count;
u8 llv; /* status led brightness no button (1..127) */
u8 hlv; /* status led brightness button pressed (1..127) */
u8 img_lum; /* OLED matrix display brightness */
+ u8 max_llv; /* maximum brightness of LED (llv) */
+ u8 max_hlv; /* maximum brightness of LED (hlv) */
} led;
struct wacom_battery battery;
bool resources;
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 1d79215a7968..c5d518da0d0f 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -647,6 +647,9 @@ static int wacom_led_control(struct wacom *wacom)
unsigned char report_id = WAC_CMD_LED_CONTROL;
int buf_size = 9;
+ if (!hid_get_drvdata(wacom->hdev))
+ return -ENODEV;
+
if (!wacom->led.groups)
return -ENOTSUPP;
@@ -966,31 +969,194 @@ static int wacom_devm_sysfs_create_group(struct wacom *wacom,
group);
}
+static enum led_brightness wacom_leds_brightness_get(struct wacom_led *led)
+{
+ struct wacom *wacom = led->wacom;
+
+ if (wacom->led.max_hlv)
+ return led->hlv * LED_FULL / wacom->led.max_hlv;
+
+ if (wacom->led.max_llv)
+ return led->llv * LED_FULL / wacom->led.max_llv;
+
+ /* device doesn't support brightness tuning */
+ return LED_FULL;
+}
+
+static enum led_brightness __wacom_led_brightness_get(struct led_classdev *cdev)
+{
+ struct wacom_led *led = container_of(cdev, struct wacom_led, cdev);
+ struct wacom *wacom = led->wacom;
+
+ if (wacom->led.groups[led->group].select != led->id)
+ return LED_OFF;
+
+ return wacom_leds_brightness_get(led);
+}
+
+static int wacom_led_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct wacom_led *led = container_of(cdev, struct wacom_led, cdev);
+ struct wacom *wacom = led->wacom;
+ int error;
+
+ mutex_lock(&wacom->lock);
+
+ if (!wacom->led.groups || (brightness == LED_OFF &&
+ wacom->led.groups[led->group].select != led->id)) {
+ error = 0;
+ goto out;
+ }
+
+ led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL;
+ led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL;
+
+ wacom->led.groups[led->group].select = led->id;
+
+ error = wacom_led_control(wacom);
+
+out:
+ mutex_unlock(&wacom->lock);
+
+ return error;
+}
+
+static void wacom_led_readonly_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+}
+
+static int wacom_led_register_one(struct device *dev, struct wacom *wacom,
+ struct wacom_led *led, unsigned int group,
+ unsigned int id, bool read_only)
+{
+ int error;
+ char *name;
+
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ "%s::wacom-%d.%d",
+ dev_name(dev),
+ group,
+ id);
+ if (!name)
+ return -ENOMEM;
+
+ led->group = group;
+ led->id = id;
+ led->wacom = wacom;
+ led->llv = wacom->led.llv;
+ led->hlv = wacom->led.hlv;
+ led->cdev.name = name;
+ led->cdev.max_brightness = LED_FULL;
+ led->cdev.flags = LED_HW_PLUGGABLE;
+ led->cdev.brightness_get = __wacom_led_brightness_get;
+ if (!read_only)
+ led->cdev.brightness_set_blocking = wacom_led_brightness_set;
+ else
+ led->cdev.brightness_set = wacom_led_readonly_brightness_set;
+
+ error = devm_led_classdev_register(dev, &led->cdev);
+ if (error) {
+ hid_err(wacom->hdev,
+ "failed to register LED %s: %d\n",
+ led->cdev.name, error);
+ led->cdev.name = NULL;
+ return error;
+ }
+
+ return 0;
+}
+
+static int wacom_led_groups_alloc_and_register_one(struct device *dev,
+ struct wacom *wacom,
+ int group_id, int count,
+ bool read_only)
+{
+ struct wacom_led *leds;
+ int i, error;
+
+ if (group_id >= wacom->led.count || count <= 0)
+ return -EINVAL;
+
+ if (!devres_open_group(dev, &wacom->led.groups[group_id], GFP_KERNEL))
+ return -ENOMEM;
+
+ leds = devm_kzalloc(dev, sizeof(struct wacom_led) * count, GFP_KERNEL);
+ if (!leds) {
+ error = -ENOMEM;
+ goto err;
+ }
+
+ wacom->led.groups[group_id].leds = leds;
+ wacom->led.groups[group_id].count = count;
+
+ for (i = 0; i < count; i++) {
+ error = wacom_led_register_one(dev, wacom, &leds[i],
+ group_id, i, read_only);
+ if (error)
+ goto err;
+ }
+
+ devres_remove_group(dev, &wacom->led.groups[group_id]);
+ return 0;
+
+err:
+ devres_release_group(dev, &wacom->led.groups[group_id]);
+ return error;
+}
+
static void wacom_led_groups_release(void *data)
{
struct wacom *wacom = data;
wacom->led.groups = NULL;
+ wacom->led.count = 0;
}
static int wacom_led_groups_allocate(struct wacom *wacom, int count)
{
+ struct device *dev = &wacom->hdev->dev;
struct wacom_group_leds *groups;
int error;
- groups = devm_kzalloc(&wacom->hdev->dev,
- sizeof(struct wacom_group_leds) * count,
+ groups = devm_kzalloc(dev, sizeof(struct wacom_group_leds) * count,
GFP_KERNEL);
if (!groups)
return -ENOMEM;
- error = devm_add_action_or_reset(&wacom->hdev->dev,
- wacom_led_groups_release,
- wacom);
+ error = devm_add_action_or_reset(dev, wacom_led_groups_release, wacom);
if (error)
return error;
wacom->led.groups = groups;
+ wacom->led.count = count;
+
+ return 0;
+}
+
+static int wacom_leds_alloc_and_register(struct wacom *wacom, int group_count,
+ int led_per_group, bool read_only)
+{
+ struct device *dev;
+ int i, error;
+
+ if (!wacom->wacom_wac.pad_input)
+ return -EINVAL;
+
+ dev = &wacom->wacom_wac.pad_input->dev;
+
+ error = wacom_led_groups_allocate(wacom, group_count);
+ if (error)
+ return error;
+
+ for (i = 0; i < group_count; i++) {
+ error = wacom_led_groups_alloc_and_register_one(dev, wacom, i,
+ led_per_group,
+ read_only);
+ if (error)
+ return error;
+ }
return 0;
}
@@ -1010,9 +1176,11 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOS4L:
wacom->led.llv = 10;
wacom->led.hlv = 20;
+ wacom->led.max_llv = 127;
+ wacom->led.max_hlv = 127;
wacom->led.img_lum = 10;
- error = wacom_led_groups_allocate(wacom, 1);
+ error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
if (error) {
hid_err(wacom->hdev,
"cannot create leds err: %d\n", error);
@@ -1029,7 +1197,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
wacom->led.hlv = 0;
wacom->led.img_lum = 0;
- error = wacom_led_groups_allocate(wacom, 2);
+ error = wacom_leds_alloc_and_register(wacom, 2, 4, false);
if (error) {
hid_err(wacom->hdev,
"cannot create leds err: %d\n", error);
@@ -1047,10 +1215,9 @@ static int wacom_initialize_leds(struct wacom *wacom)
case INTUOSPM:
case INTUOSPL:
wacom->led.llv = 32;
- wacom->led.hlv = 0;
- wacom->led.img_lum = 0;
+ wacom->led.max_llv = 96;
- error = wacom_led_groups_allocate(wacom, 1);
+ error = wacom_leds_alloc_and_register(wacom, 1, 4, false);
if (error) {
hid_err(wacom->hdev,
"cannot create leds err: %d\n", error);
@@ -1062,6 +1229,8 @@ static int wacom_initialize_leds(struct wacom *wacom)
break;
case REMOTE:
+ wacom->led.llv = 255;
+ wacom->led.max_llv = 255;
error = wacom_led_groups_allocate(wacom, 5);
if (error) {
hid_err(wacom->hdev,
@@ -1987,6 +2156,12 @@ static int wacom_remote_create_one(struct wacom *wacom, u32 serial,
if (error)
goto fail;
+ error = wacom_led_groups_alloc_and_register_one(
+ &remote->remotes[index].input->dev,
+ wacom, index, 3, true);
+ if (error)
+ goto fail;
+
remote->remotes[index].registered = true;
devres_close_group(dev, &remote->remotes[index]);