perf tools: Don't clone maps from parent when synthesizing forks
[linux/fpc-iii.git] / drivers / leds / trigger / ledtrig-pattern.c
blobce7acd115dd8da7578b4fc8d3fc3733692d90743
1 // SPDX-License-Identifier: GPL-2.0
3 /*
4 * LED pattern trigger
6 * Idea discussed with Pavel Machek. Raphael Teysseyre implemented
7 * the first version, Baolin Wang simplified and improved the approach.
8 */
10 #include <linux/kernel.h>
11 #include <linux/leds.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/slab.h>
15 #include <linux/timer.h>
17 #define MAX_PATTERNS 1024
19 * When doing gradual dimming, the led brightness will be updated
20 * every 50 milliseconds.
22 #define UPDATE_INTERVAL 50
24 struct pattern_trig_data {
25 struct led_classdev *led_cdev;
26 struct led_pattern patterns[MAX_PATTERNS];
27 struct led_pattern *curr;
28 struct led_pattern *next;
29 struct mutex lock;
30 u32 npatterns;
31 int repeat;
32 int last_repeat;
33 int delta_t;
34 bool is_indefinite;
35 bool is_hw_pattern;
36 struct timer_list timer;
39 static void pattern_trig_update_patterns(struct pattern_trig_data *data)
41 data->curr = data->next;
42 if (!data->is_indefinite && data->curr == data->patterns)
43 data->repeat--;
45 if (data->next == data->patterns + data->npatterns - 1)
46 data->next = data->patterns;
47 else
48 data->next++;
50 data->delta_t = 0;
53 static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
55 int step_brightness;
58 * If current tuple's duration is less than the dimming interval,
59 * we should treat it as a step change of brightness instead of
60 * doing gradual dimming.
62 if (data->delta_t == 0 || data->curr->delta_t < UPDATE_INTERVAL)
63 return data->curr->brightness;
65 step_brightness = abs(data->next->brightness - data->curr->brightness);
66 step_brightness = data->delta_t * step_brightness / data->curr->delta_t;
68 if (data->next->brightness > data->curr->brightness)
69 return data->curr->brightness + step_brightness;
70 else
71 return data->curr->brightness - step_brightness;
74 static void pattern_trig_timer_function(struct timer_list *t)
76 struct pattern_trig_data *data = from_timer(data, t, timer);
78 mutex_lock(&data->lock);
80 for (;;) {
81 if (!data->is_indefinite && !data->repeat)
82 break;
84 if (data->curr->brightness == data->next->brightness) {
85 /* Step change of brightness */
86 led_set_brightness(data->led_cdev,
87 data->curr->brightness);
88 mod_timer(&data->timer,
89 jiffies + msecs_to_jiffies(data->curr->delta_t));
91 /* Skip the tuple with zero duration */
92 pattern_trig_update_patterns(data);
93 /* Select next tuple */
94 pattern_trig_update_patterns(data);
95 } else {
96 /* Gradual dimming */
99 * If the accumulation time is larger than current
100 * tuple's duration, we should go next one and re-check
101 * if we repeated done.
103 if (data->delta_t > data->curr->delta_t) {
104 pattern_trig_update_patterns(data);
105 continue;
108 led_set_brightness(data->led_cdev,
109 pattern_trig_compute_brightness(data));
110 mod_timer(&data->timer,
111 jiffies + msecs_to_jiffies(UPDATE_INTERVAL));
113 /* Accumulate the gradual dimming time */
114 data->delta_t += UPDATE_INTERVAL;
117 break;
120 mutex_unlock(&data->lock);
123 static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
125 struct pattern_trig_data *data = led_cdev->trigger_data;
127 if (!data->npatterns)
128 return 0;
130 if (data->is_hw_pattern) {
131 return led_cdev->pattern_set(led_cdev, data->patterns,
132 data->npatterns, data->repeat);
135 /* At least 2 tuples for software pattern. */
136 if (data->npatterns < 2)
137 return -EINVAL;
139 data->delta_t = 0;
140 data->curr = data->patterns;
141 data->next = data->patterns + 1;
142 data->timer.expires = jiffies;
143 add_timer(&data->timer);
145 return 0;
148 static ssize_t repeat_show(struct device *dev, struct device_attribute *attr,
149 char *buf)
151 struct led_classdev *led_cdev = dev_get_drvdata(dev);
152 struct pattern_trig_data *data = led_cdev->trigger_data;
153 int repeat;
155 mutex_lock(&data->lock);
157 repeat = data->last_repeat;
159 mutex_unlock(&data->lock);
161 return scnprintf(buf, PAGE_SIZE, "%d\n", repeat);
164 static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
165 const char *buf, size_t count)
167 struct led_classdev *led_cdev = dev_get_drvdata(dev);
168 struct pattern_trig_data *data = led_cdev->trigger_data;
169 int err, res;
171 err = kstrtos32(buf, 10, &res);
172 if (err)
173 return err;
175 /* Number 0 and negative numbers except -1 are invalid. */
176 if (res < -1 || res == 0)
177 return -EINVAL;
180 * Clear previous patterns' performence firstly, and remove the timer
181 * without mutex lock to avoid dead lock.
183 del_timer_sync(&data->timer);
185 mutex_lock(&data->lock);
187 if (data->is_hw_pattern)
188 led_cdev->pattern_clear(led_cdev);
190 data->last_repeat = data->repeat = res;
191 /* -1 means repeat indefinitely */
192 if (data->repeat == -1)
193 data->is_indefinite = true;
194 else
195 data->is_indefinite = false;
197 err = pattern_trig_start_pattern(led_cdev);
199 mutex_unlock(&data->lock);
200 return err < 0 ? err : count;
203 static DEVICE_ATTR_RW(repeat);
205 static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
206 char *buf, bool hw_pattern)
208 ssize_t count = 0;
209 int i;
211 mutex_lock(&data->lock);
213 if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern))
214 goto out;
216 for (i = 0; i < data->npatterns; i++) {
217 count += scnprintf(buf + count, PAGE_SIZE - count,
218 "%d %u ",
219 data->patterns[i].brightness,
220 data->patterns[i].delta_t);
223 buf[count - 1] = '\n';
225 out:
226 mutex_unlock(&data->lock);
227 return count;
230 static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
231 const char *buf, size_t count,
232 bool hw_pattern)
234 struct pattern_trig_data *data = led_cdev->trigger_data;
235 int ccount, cr, offset = 0, err = 0;
238 * Clear previous patterns' performence firstly, and remove the timer
239 * without mutex lock to avoid dead lock.
241 del_timer_sync(&data->timer);
243 mutex_lock(&data->lock);
245 if (data->is_hw_pattern)
246 led_cdev->pattern_clear(led_cdev);
248 data->is_hw_pattern = hw_pattern;
249 data->npatterns = 0;
251 while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
252 cr = 0;
253 ccount = sscanf(buf + offset, "%d %u %n",
254 &data->patterns[data->npatterns].brightness,
255 &data->patterns[data->npatterns].delta_t, &cr);
256 if (ccount != 2) {
257 data->npatterns = 0;
258 err = -EINVAL;
259 goto out;
262 offset += cr;
263 data->npatterns++;
266 err = pattern_trig_start_pattern(led_cdev);
267 if (err)
268 data->npatterns = 0;
270 out:
271 mutex_unlock(&data->lock);
272 return err < 0 ? err : count;
275 static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
276 char *buf)
278 struct led_classdev *led_cdev = dev_get_drvdata(dev);
279 struct pattern_trig_data *data = led_cdev->trigger_data;
281 return pattern_trig_show_patterns(data, buf, false);
284 static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
285 const char *buf, size_t count)
287 struct led_classdev *led_cdev = dev_get_drvdata(dev);
289 return pattern_trig_store_patterns(led_cdev, buf, count, false);
292 static DEVICE_ATTR_RW(pattern);
294 static ssize_t hw_pattern_show(struct device *dev,
295 struct device_attribute *attr, char *buf)
297 struct led_classdev *led_cdev = dev_get_drvdata(dev);
298 struct pattern_trig_data *data = led_cdev->trigger_data;
300 return pattern_trig_show_patterns(data, buf, true);
303 static ssize_t hw_pattern_store(struct device *dev,
304 struct device_attribute *attr,
305 const char *buf, size_t count)
307 struct led_classdev *led_cdev = dev_get_drvdata(dev);
309 return pattern_trig_store_patterns(led_cdev, buf, count, true);
312 static DEVICE_ATTR_RW(hw_pattern);
314 static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
315 struct attribute *attr, int index)
317 struct device *dev = container_of(kobj, struct device, kobj);
318 struct led_classdev *led_cdev = dev_get_drvdata(dev);
320 if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
321 return attr->mode;
322 else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
323 return attr->mode;
325 return 0;
328 static struct attribute *pattern_trig_attrs[] = {
329 &dev_attr_pattern.attr,
330 &dev_attr_hw_pattern.attr,
331 &dev_attr_repeat.attr,
332 NULL
335 static const struct attribute_group pattern_trig_group = {
336 .attrs = pattern_trig_attrs,
337 .is_visible = pattern_trig_attrs_mode,
340 static const struct attribute_group *pattern_trig_groups[] = {
341 &pattern_trig_group,
342 NULL,
345 static int pattern_trig_activate(struct led_classdev *led_cdev)
347 struct pattern_trig_data *data;
349 data = kzalloc(sizeof(*data), GFP_KERNEL);
350 if (!data)
351 return -ENOMEM;
353 if (!!led_cdev->pattern_set ^ !!led_cdev->pattern_clear) {
354 dev_warn(led_cdev->dev,
355 "Hardware pattern ops validation failed\n");
356 led_cdev->pattern_set = NULL;
357 led_cdev->pattern_clear = NULL;
360 data->is_indefinite = true;
361 data->last_repeat = -1;
362 mutex_init(&data->lock);
363 data->led_cdev = led_cdev;
364 led_set_trigger_data(led_cdev, data);
365 timer_setup(&data->timer, pattern_trig_timer_function, 0);
366 led_cdev->activated = true;
368 return 0;
371 static void pattern_trig_deactivate(struct led_classdev *led_cdev)
373 struct pattern_trig_data *data = led_cdev->trigger_data;
375 if (!led_cdev->activated)
376 return;
378 if (led_cdev->pattern_clear)
379 led_cdev->pattern_clear(led_cdev);
381 del_timer_sync(&data->timer);
383 led_set_brightness(led_cdev, LED_OFF);
384 kfree(data);
385 led_cdev->activated = false;
388 static struct led_trigger pattern_led_trigger = {
389 .name = "pattern",
390 .activate = pattern_trig_activate,
391 .deactivate = pattern_trig_deactivate,
392 .groups = pattern_trig_groups,
395 static int __init pattern_trig_init(void)
397 return led_trigger_register(&pattern_led_trigger);
400 static void __exit pattern_trig_exit(void)
402 led_trigger_unregister(&pattern_led_trigger);
405 module_init(pattern_trig_init);
406 module_exit(pattern_trig_exit);
408 MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com");
409 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org");
410 MODULE_DESCRIPTION("LED Pattern trigger");
411 MODULE_LICENSE("GPL v2");