1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Backlight emulation LED trigger 4 * 5 * Copyright 2008 (C) Rodolfo Giometti <giometti@linux.it> 6 * Copyright 2008 (C) Eurotech S.p.A. <info@eurotech.it> 7 */ 8 9 #include <linux/module.h> 10 #include <linux/kernel.h> 11 #include <linux/slab.h> 12 #include <linux/init.h> 13 #include <linux/leds.h> 14 #include "../leds.h" 15 16 #define BLANK 1 17 #define UNBLANK 0 18 19 struct bl_trig_notifier { 20 struct led_classdev *led; 21 int brightness; 22 int old_status; 23 unsigned invert; 24 25 struct list_head entry; 26 }; 27 28 static DEFINE_MUTEX(ledtrig_backlight_list_mutex); 29 static LIST_HEAD(ledtrig_backlight_list); 30 31 static void ledtrig_backlight_notify_blank(struct bl_trig_notifier *n, int new_status) 32 { 33 struct led_classdev *led = n->led; 34 35 if (new_status == n->old_status) 36 return; 37 38 if ((n->old_status == UNBLANK) ^ n->invert) { 39 n->brightness = led->brightness; 40 led_set_brightness_nosleep(led, LED_OFF); 41 } else { 42 led_set_brightness_nosleep(led, n->brightness); 43 } 44 45 n->old_status = new_status; 46 } 47 48 void ledtrig_backlight_blank(bool blank) 49 { 50 struct bl_trig_notifier *n; 51 int new_status = blank ? BLANK : UNBLANK; 52 53 guard(mutex)(&ledtrig_backlight_list_mutex); 54 55 list_for_each_entry(n, &ledtrig_backlight_list, entry) 56 ledtrig_backlight_notify_blank(n, new_status); 57 } 58 EXPORT_SYMBOL(ledtrig_backlight_blank); 59 60 static ssize_t bl_trig_invert_show(struct device *dev, 61 struct device_attribute *attr, char *buf) 62 { 63 struct bl_trig_notifier *n = led_trigger_get_drvdata(dev); 64 65 return sprintf(buf, "%u\n", n->invert); 66 } 67 68 static ssize_t bl_trig_invert_store(struct device *dev, 69 struct device_attribute *attr, const char *buf, size_t num) 70 { 71 struct led_classdev *led = led_trigger_get_led(dev); 72 struct bl_trig_notifier *n = led_trigger_get_drvdata(dev); 73 unsigned long invert; 74 int ret; 75 76 ret = kstrtoul(buf, 10, &invert); 77 if (ret < 0) 78 return ret; 79 80 if (invert > 1) 81 return -EINVAL; 82 83 n->invert = invert; 84 85 /* After inverting, we need to update the LED. */ 86 if ((n->old_status == BLANK) ^ n->invert) 87 led_set_brightness_nosleep(led, LED_OFF); 88 else 89 led_set_brightness_nosleep(led, n->brightness); 90 91 return num; 92 } 93 static DEVICE_ATTR(inverted, 0644, bl_trig_invert_show, bl_trig_invert_store); 94 95 static struct attribute *bl_trig_attrs[] = { 96 &dev_attr_inverted.attr, 97 NULL, 98 }; 99 ATTRIBUTE_GROUPS(bl_trig); 100 101 static int bl_trig_activate(struct led_classdev *led) 102 { 103 struct bl_trig_notifier *n; 104 105 n = kzalloc(sizeof(struct bl_trig_notifier), GFP_KERNEL); 106 if (!n) 107 return -ENOMEM; 108 led_set_trigger_data(led, n); 109 110 n->led = led; 111 n->brightness = led->brightness; 112 n->old_status = UNBLANK; 113 114 guard(mutex)(&ledtrig_backlight_list_mutex); 115 list_add(&n->entry, &ledtrig_backlight_list); 116 117 return 0; 118 } 119 120 static void bl_trig_deactivate(struct led_classdev *led) 121 { 122 struct bl_trig_notifier *n = led_get_trigger_data(led); 123 124 guard(mutex)(&ledtrig_backlight_list_mutex); 125 list_del(&n->entry); 126 127 kfree(n); 128 } 129 130 static struct led_trigger bl_led_trigger = { 131 .name = "backlight", 132 .activate = bl_trig_activate, 133 .deactivate = bl_trig_deactivate, 134 .groups = bl_trig_groups, 135 }; 136 module_led_trigger(bl_led_trigger); 137 138 MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 139 MODULE_DESCRIPTION("Backlight emulation LED trigger"); 140 MODULE_LICENSE("GPL v2"); 141