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
ledtrig_backlight_notify_blank(struct bl_trig_notifier * n,int new_status)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
ledtrig_backlight_blank(bool blank)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
bl_trig_invert_show(struct device * dev,struct device_attribute * attr,char * buf)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
bl_trig_invert_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t num)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
bl_trig_activate(struct led_classdev * led)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
bl_trig_deactivate(struct led_classdev * led)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