Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: wacom: Improve behavior of non-standard LED brightness values

Assigning a non-standard brightness value to an LED can cause the value
to slowly drift downward over time as the effects of integer division
accumulate. Each time that an LED is triggered, a series of set and get
calls occur. For example, if we assume a tablet with max_hlv = 100, then
when brightness is set to "200" through sysfs, the hlv value written to
hardware will be `200*100/255 = 78`. If the LED trigger is later activated,
the hlv value will be used to determine the brightness: `78*255/100 = 198`.
This lower brightness then used to set the brightness of the next LED.
However, `198*100/255 = 77`, so the next LED ends up slightly dimmer.
Each subsequent trigger activation will cause the brightness to continue
drifting down until we reach a point where the result of integer divsion
does not introduce any new error.

This commit corrects the issue by being more careful about how we handle
scaling between the two ranges (0..max_{h,l}lv) and (0..LED_FULL).

Signed-off-by: Jason Gerecke <jason.gerecke@wacom.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>

authored by

Jason Gerecke and committed by
Jiri Kosina
4f4ab4bc 2a770b49

+12 -4
+8
drivers/hid/wacom.h
··· 218 218 return value & (1 << (n - 1)) ? value & (~(~0U << n)) : value; 219 219 } 220 220 221 + static inline u32 wacom_rescale(u32 value, u32 in_max, u32 out_max) 222 + { 223 + if (in_max == 0 || out_max == 0) 224 + return 0; 225 + value = clamp(value, 0, in_max); 226 + return DIV_ROUND_CLOSEST(value * out_max, in_max); 227 + } 228 + 221 229 extern const struct hid_device_id wacom_ids[]; 222 230 223 231 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
+4 -4
drivers/hid/wacom_sys.c
··· 1302 1302 struct wacom *wacom = led->wacom; 1303 1303 1304 1304 if (wacom->led.max_hlv) 1305 - return led->hlv * LED_FULL / wacom->led.max_hlv; 1305 + return wacom_rescale(led->hlv, wacom->led.max_hlv, LED_FULL); 1306 1306 1307 1307 if (wacom->led.max_llv) 1308 - return led->llv * LED_FULL / wacom->led.max_llv; 1308 + return wacom_rescale(led->llv, wacom->led.max_llv, LED_FULL); 1309 1309 1310 1310 /* device doesn't support brightness tuning */ 1311 1311 return LED_FULL; ··· 1337 1337 goto out; 1338 1338 } 1339 1339 1340 - led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL; 1341 - led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL; 1340 + led->llv = wacom->led.llv = wacom_rescale(brightness, LED_FULL, wacom->led.max_llv); 1341 + led->hlv = wacom->led.hlv = wacom_rescale(brightness, LED_FULL, wacom->led.max_hlv); 1342 1342 1343 1343 wacom->led.groups[led->group].select = led->id; 1344 1344