1 // SPDX-License-Identifier: GPL-2.0
6 * Idea discussed with Pavel Machek. Raphael Teysseyre implemented
7 * the first version, Baolin Wang simplified and improved the approach.
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>
16 #include <linux/hrtimer.h>
18 #define MAX_PATTERNS 1024
20 * When doing gradual dimming, the led brightness will be updated
21 * every 50 milliseconds.
23 #define UPDATE_INTERVAL 50
26 PATTERN_TYPE_SW
, /* Use standard timer for software pattern */
27 PATTERN_TYPE_HR
, /* Use hrtimer for software pattern */
28 PATTERN_TYPE_HW
, /* Hardware pattern */
31 struct pattern_trig_data
{
32 struct led_classdev
*led_cdev
;
33 struct led_pattern patterns
[MAX_PATTERNS
];
34 struct led_pattern
*curr
;
35 struct led_pattern
*next
;
42 enum pattern_type type
;
43 struct timer_list timer
;
44 struct hrtimer hrtimer
;
47 static void pattern_trig_update_patterns(struct pattern_trig_data
*data
)
49 data
->curr
= data
->next
;
50 if (!data
->is_indefinite
&& data
->curr
== data
->patterns
)
53 if (data
->next
== data
->patterns
+ data
->npatterns
- 1)
54 data
->next
= data
->patterns
;
61 static int pattern_trig_compute_brightness(struct pattern_trig_data
*data
)
66 * If current tuple's duration is less than the dimming interval,
67 * we should treat it as a step change of brightness instead of
68 * doing gradual dimming.
70 if (data
->delta_t
== 0 || data
->curr
->delta_t
< UPDATE_INTERVAL
)
71 return data
->curr
->brightness
;
73 step_brightness
= abs(data
->next
->brightness
- data
->curr
->brightness
);
74 step_brightness
= data
->delta_t
* step_brightness
/ data
->curr
->delta_t
;
76 if (data
->next
->brightness
> data
->curr
->brightness
)
77 return data
->curr
->brightness
+ step_brightness
;
79 return data
->curr
->brightness
- step_brightness
;
82 static void pattern_trig_timer_start(struct pattern_trig_data
*data
)
84 if (data
->type
== PATTERN_TYPE_HR
) {
85 hrtimer_start(&data
->hrtimer
, ns_to_ktime(0), HRTIMER_MODE_REL
);
87 data
->timer
.expires
= jiffies
;
88 add_timer(&data
->timer
);
92 static void pattern_trig_timer_cancel(struct pattern_trig_data
*data
)
94 if (data
->type
== PATTERN_TYPE_HR
)
95 hrtimer_cancel(&data
->hrtimer
);
97 del_timer_sync(&data
->timer
);
100 static void pattern_trig_timer_restart(struct pattern_trig_data
*data
,
101 unsigned long interval
)
103 if (data
->type
== PATTERN_TYPE_HR
)
104 hrtimer_forward_now(&data
->hrtimer
, ms_to_ktime(interval
));
106 mod_timer(&data
->timer
, jiffies
+ msecs_to_jiffies(interval
));
109 static void pattern_trig_timer_common_function(struct pattern_trig_data
*data
)
112 if (!data
->is_indefinite
&& !data
->repeat
)
115 if (data
->curr
->brightness
== data
->next
->brightness
) {
116 /* Step change of brightness */
117 led_set_brightness(data
->led_cdev
,
118 data
->curr
->brightness
);
119 pattern_trig_timer_restart(data
, data
->curr
->delta_t
);
120 if (!data
->next
->delta_t
) {
121 /* Skip the tuple with zero duration */
122 pattern_trig_update_patterns(data
);
124 /* Select next tuple */
125 pattern_trig_update_patterns(data
);
127 /* Gradual dimming */
130 * If the accumulation time is larger than current
131 * tuple's duration, we should go next one and re-check
132 * if we repeated done.
134 if (data
->delta_t
> data
->curr
->delta_t
) {
135 pattern_trig_update_patterns(data
);
139 led_set_brightness(data
->led_cdev
,
140 pattern_trig_compute_brightness(data
));
141 pattern_trig_timer_restart(data
, UPDATE_INTERVAL
);
143 /* Accumulate the gradual dimming time */
144 data
->delta_t
+= UPDATE_INTERVAL
;
151 static void pattern_trig_timer_function(struct timer_list
*t
)
153 struct pattern_trig_data
*data
= from_timer(data
, t
, timer
);
155 return pattern_trig_timer_common_function(data
);
158 static enum hrtimer_restart
pattern_trig_hrtimer_function(struct hrtimer
*t
)
160 struct pattern_trig_data
*data
=
161 container_of(t
, struct pattern_trig_data
, hrtimer
);
163 pattern_trig_timer_common_function(data
);
164 if (!data
->is_indefinite
&& !data
->repeat
)
165 return HRTIMER_NORESTART
;
167 return HRTIMER_RESTART
;
170 static int pattern_trig_start_pattern(struct led_classdev
*led_cdev
)
172 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
174 if (!data
->npatterns
)
177 if (data
->type
== PATTERN_TYPE_HW
) {
178 return led_cdev
->pattern_set(led_cdev
, data
->patterns
,
179 data
->npatterns
, data
->repeat
);
182 /* At least 2 tuples for software pattern. */
183 if (data
->npatterns
< 2)
187 data
->curr
= data
->patterns
;
188 data
->next
= data
->patterns
+ 1;
189 pattern_trig_timer_start(data
);
194 static ssize_t
repeat_show(struct device
*dev
, struct device_attribute
*attr
,
197 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
198 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
201 mutex_lock(&data
->lock
);
203 repeat
= data
->last_repeat
;
205 mutex_unlock(&data
->lock
);
207 return sysfs_emit(buf
, "%d\n", repeat
);
210 static ssize_t
repeat_store(struct device
*dev
, struct device_attribute
*attr
,
211 const char *buf
, size_t count
)
213 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
214 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
217 err
= kstrtos32(buf
, 10, &res
);
221 /* Number 0 and negative numbers except -1 are invalid. */
222 if (res
< -1 || res
== 0)
225 mutex_lock(&data
->lock
);
227 pattern_trig_timer_cancel(data
);
229 if (data
->type
== PATTERN_TYPE_HW
)
230 led_cdev
->pattern_clear(led_cdev
);
232 data
->last_repeat
= data
->repeat
= res
;
233 /* -1 means repeat indefinitely */
234 if (data
->repeat
== -1)
235 data
->is_indefinite
= true;
237 data
->is_indefinite
= false;
239 err
= pattern_trig_start_pattern(led_cdev
);
241 mutex_unlock(&data
->lock
);
242 return err
< 0 ? err
: count
;
245 static DEVICE_ATTR_RW(repeat
);
247 static ssize_t
pattern_trig_show_patterns(struct pattern_trig_data
*data
,
248 char *buf
, enum pattern_type type
)
253 mutex_lock(&data
->lock
);
255 if (!data
->npatterns
|| data
->type
!= type
)
258 for (i
= 0; i
< data
->npatterns
; i
++) {
259 count
+= scnprintf(buf
+ count
, PAGE_SIZE
- count
,
261 data
->patterns
[i
].brightness
,
262 data
->patterns
[i
].delta_t
);
265 buf
[count
- 1] = '\n';
268 mutex_unlock(&data
->lock
);
272 static int pattern_trig_store_patterns_string(struct pattern_trig_data
*data
,
273 const char *buf
, size_t count
)
275 int ccount
, cr
, offset
= 0;
277 while (offset
< count
- 1 && data
->npatterns
< MAX_PATTERNS
) {
279 ccount
= sscanf(buf
+ offset
, "%u %u %n",
280 &data
->patterns
[data
->npatterns
].brightness
,
281 &data
->patterns
[data
->npatterns
].delta_t
, &cr
);
284 data
->patterns
[data
->npatterns
].brightness
> data
->led_cdev
->max_brightness
) {
296 static int pattern_trig_store_patterns_int(struct pattern_trig_data
*data
,
297 const u32
*buf
, size_t count
)
301 for (i
= 0; i
< count
; i
+= 2) {
302 data
->patterns
[data
->npatterns
].brightness
= buf
[i
];
303 data
->patterns
[data
->npatterns
].delta_t
= buf
[i
+ 1];
310 static ssize_t
pattern_trig_store_patterns(struct led_classdev
*led_cdev
,
311 const char *buf
, const u32
*buf_int
,
312 size_t count
, enum pattern_type type
)
314 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
317 mutex_lock(&data
->lock
);
319 pattern_trig_timer_cancel(data
);
321 if (data
->type
== PATTERN_TYPE_HW
)
322 led_cdev
->pattern_clear(led_cdev
);
328 err
= pattern_trig_store_patterns_string(data
, buf
, count
);
330 err
= pattern_trig_store_patterns_int(data
, buf_int
, count
);
334 err
= pattern_trig_start_pattern(led_cdev
);
339 mutex_unlock(&data
->lock
);
340 return err
< 0 ? err
: count
;
343 static ssize_t
pattern_show(struct device
*dev
, struct device_attribute
*attr
,
346 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
347 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
349 return pattern_trig_show_patterns(data
, buf
, PATTERN_TYPE_SW
);
352 static ssize_t
pattern_store(struct device
*dev
, struct device_attribute
*attr
,
353 const char *buf
, size_t count
)
355 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
357 return pattern_trig_store_patterns(led_cdev
, buf
, NULL
, count
,
361 static DEVICE_ATTR_RW(pattern
);
363 static ssize_t
hw_pattern_show(struct device
*dev
,
364 struct device_attribute
*attr
, char *buf
)
366 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
367 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
369 return pattern_trig_show_patterns(data
, buf
, PATTERN_TYPE_HW
);
372 static ssize_t
hw_pattern_store(struct device
*dev
,
373 struct device_attribute
*attr
,
374 const char *buf
, size_t count
)
376 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
378 return pattern_trig_store_patterns(led_cdev
, buf
, NULL
, count
,
382 static DEVICE_ATTR_RW(hw_pattern
);
384 static ssize_t
hr_pattern_show(struct device
*dev
,
385 struct device_attribute
*attr
, char *buf
)
387 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
388 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
390 return pattern_trig_show_patterns(data
, buf
, PATTERN_TYPE_HR
);
393 static ssize_t
hr_pattern_store(struct device
*dev
,
394 struct device_attribute
*attr
,
395 const char *buf
, size_t count
)
397 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
399 return pattern_trig_store_patterns(led_cdev
, buf
, NULL
, count
,
403 static DEVICE_ATTR_RW(hr_pattern
);
405 static umode_t
pattern_trig_attrs_mode(struct kobject
*kobj
,
406 struct attribute
*attr
, int index
)
408 struct device
*dev
= kobj_to_dev(kobj
);
409 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
411 if (attr
== &dev_attr_repeat
.attr
|| attr
== &dev_attr_pattern
.attr
)
413 else if (attr
== &dev_attr_hr_pattern
.attr
)
415 else if (attr
== &dev_attr_hw_pattern
.attr
&& led_cdev
->pattern_set
)
421 static struct attribute
*pattern_trig_attrs
[] = {
422 &dev_attr_pattern
.attr
,
423 &dev_attr_hw_pattern
.attr
,
424 &dev_attr_hr_pattern
.attr
,
425 &dev_attr_repeat
.attr
,
429 static const struct attribute_group pattern_trig_group
= {
430 .attrs
= pattern_trig_attrs
,
431 .is_visible
= pattern_trig_attrs_mode
,
434 static const struct attribute_group
*pattern_trig_groups
[] = {
439 static void pattern_init(struct led_classdev
*led_cdev
)
441 unsigned int size
= 0;
445 pattern
= led_get_default_pattern(led_cdev
, &size
);
450 dev_warn(led_cdev
->dev
, "Expected pattern of tuples\n");
454 err
= pattern_trig_store_patterns(led_cdev
, NULL
, pattern
, size
,
457 dev_warn(led_cdev
->dev
,
458 "Pattern initialization failed with error %d\n", err
);
464 static int pattern_trig_activate(struct led_classdev
*led_cdev
)
466 struct pattern_trig_data
*data
;
468 data
= kzalloc(sizeof(*data
), GFP_KERNEL
);
472 if (!!led_cdev
->pattern_set
^ !!led_cdev
->pattern_clear
) {
473 dev_warn(led_cdev
->dev
,
474 "Hardware pattern ops validation failed\n");
475 led_cdev
->pattern_set
= NULL
;
476 led_cdev
->pattern_clear
= NULL
;
479 data
->type
= PATTERN_TYPE_SW
;
480 data
->is_indefinite
= true;
481 data
->last_repeat
= -1;
482 mutex_init(&data
->lock
);
483 data
->led_cdev
= led_cdev
;
484 led_set_trigger_data(led_cdev
, data
);
485 timer_setup(&data
->timer
, pattern_trig_timer_function
, 0);
486 hrtimer_init(&data
->hrtimer
, CLOCK_MONOTONIC
, HRTIMER_MODE_REL
);
487 data
->hrtimer
.function
= pattern_trig_hrtimer_function
;
488 led_cdev
->activated
= true;
490 if (led_cdev
->flags
& LED_INIT_DEFAULT_TRIGGER
) {
491 pattern_init(led_cdev
);
493 * Mark as initialized even on pattern_init() error because
494 * any consecutive call to it would produce the same error.
496 led_cdev
->flags
&= ~LED_INIT_DEFAULT_TRIGGER
;
502 static void pattern_trig_deactivate(struct led_classdev
*led_cdev
)
504 struct pattern_trig_data
*data
= led_cdev
->trigger_data
;
506 if (!led_cdev
->activated
)
509 if (led_cdev
->pattern_clear
)
510 led_cdev
->pattern_clear(led_cdev
);
512 timer_shutdown_sync(&data
->timer
);
513 hrtimer_cancel(&data
->hrtimer
);
515 led_set_brightness(led_cdev
, LED_OFF
);
517 led_cdev
->activated
= false;
520 static struct led_trigger pattern_led_trigger
= {
522 .activate
= pattern_trig_activate
,
523 .deactivate
= pattern_trig_deactivate
,
524 .groups
= pattern_trig_groups
,
527 static int __init
pattern_trig_init(void)
529 return led_trigger_register(&pattern_led_trigger
);
532 static void __exit
pattern_trig_exit(void)
534 led_trigger_unregister(&pattern_led_trigger
);
537 module_init(pattern_trig_init
);
538 module_exit(pattern_trig_exit
);
540 MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com>");
541 MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
542 MODULE_DESCRIPTION("LED Pattern trigger");
543 MODULE_LICENSE("GPL v2");