1*5fd752b6SBaolin Wang // SPDX-License-Identifier: GPL-2.0
2*5fd752b6SBaolin Wang 
3*5fd752b6SBaolin Wang /*
4*5fd752b6SBaolin Wang  * LED pattern trigger
5*5fd752b6SBaolin Wang  *
6*5fd752b6SBaolin Wang  * Idea discussed with Pavel Machek. Raphael Teysseyre implemented
7*5fd752b6SBaolin Wang  * the first version, Baolin Wang simplified and improved the approach.
8*5fd752b6SBaolin Wang  */
9*5fd752b6SBaolin Wang 
10*5fd752b6SBaolin Wang #include <linux/kernel.h>
11*5fd752b6SBaolin Wang #include <linux/leds.h>
12*5fd752b6SBaolin Wang #include <linux/module.h>
13*5fd752b6SBaolin Wang #include <linux/mutex.h>
14*5fd752b6SBaolin Wang #include <linux/slab.h>
15*5fd752b6SBaolin Wang #include <linux/timer.h>
16*5fd752b6SBaolin Wang 
17*5fd752b6SBaolin Wang #define MAX_PATTERNS		1024
18*5fd752b6SBaolin Wang /*
19*5fd752b6SBaolin Wang  * When doing gradual dimming, the led brightness will be updated
20*5fd752b6SBaolin Wang  * every 50 milliseconds.
21*5fd752b6SBaolin Wang  */
22*5fd752b6SBaolin Wang #define UPDATE_INTERVAL		50
23*5fd752b6SBaolin Wang 
24*5fd752b6SBaolin Wang struct pattern_trig_data {
25*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev;
26*5fd752b6SBaolin Wang 	struct led_pattern patterns[MAX_PATTERNS];
27*5fd752b6SBaolin Wang 	struct led_pattern *curr;
28*5fd752b6SBaolin Wang 	struct led_pattern *next;
29*5fd752b6SBaolin Wang 	struct mutex lock;
30*5fd752b6SBaolin Wang 	u32 npatterns;
31*5fd752b6SBaolin Wang 	int repeat;
32*5fd752b6SBaolin Wang 	int last_repeat;
33*5fd752b6SBaolin Wang 	int delta_t;
34*5fd752b6SBaolin Wang 	bool is_indefinite;
35*5fd752b6SBaolin Wang 	bool is_hw_pattern;
36*5fd752b6SBaolin Wang 	struct timer_list timer;
37*5fd752b6SBaolin Wang };
38*5fd752b6SBaolin Wang 
39*5fd752b6SBaolin Wang static void pattern_trig_update_patterns(struct pattern_trig_data *data)
40*5fd752b6SBaolin Wang {
41*5fd752b6SBaolin Wang 	data->curr = data->next;
42*5fd752b6SBaolin Wang 	if (!data->is_indefinite && data->curr == data->patterns)
43*5fd752b6SBaolin Wang 		data->repeat--;
44*5fd752b6SBaolin Wang 
45*5fd752b6SBaolin Wang 	if (data->next == data->patterns + data->npatterns - 1)
46*5fd752b6SBaolin Wang 		data->next = data->patterns;
47*5fd752b6SBaolin Wang 	else
48*5fd752b6SBaolin Wang 		data->next++;
49*5fd752b6SBaolin Wang 
50*5fd752b6SBaolin Wang 	data->delta_t = 0;
51*5fd752b6SBaolin Wang }
52*5fd752b6SBaolin Wang 
53*5fd752b6SBaolin Wang static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
54*5fd752b6SBaolin Wang {
55*5fd752b6SBaolin Wang 	int step_brightness;
56*5fd752b6SBaolin Wang 
57*5fd752b6SBaolin Wang 	/*
58*5fd752b6SBaolin Wang 	 * If current tuple's duration is less than the dimming interval,
59*5fd752b6SBaolin Wang 	 * we should treat it as a step change of brightness instead of
60*5fd752b6SBaolin Wang 	 * doing gradual dimming.
61*5fd752b6SBaolin Wang 	 */
62*5fd752b6SBaolin Wang 	if (data->delta_t == 0 || data->curr->delta_t < UPDATE_INTERVAL)
63*5fd752b6SBaolin Wang 		return data->curr->brightness;
64*5fd752b6SBaolin Wang 
65*5fd752b6SBaolin Wang 	step_brightness = abs(data->next->brightness - data->curr->brightness);
66*5fd752b6SBaolin Wang 	step_brightness = data->delta_t * step_brightness / data->curr->delta_t;
67*5fd752b6SBaolin Wang 
68*5fd752b6SBaolin Wang 	if (data->next->brightness > data->curr->brightness)
69*5fd752b6SBaolin Wang 		return data->curr->brightness + step_brightness;
70*5fd752b6SBaolin Wang 	else
71*5fd752b6SBaolin Wang 		return data->curr->brightness - step_brightness;
72*5fd752b6SBaolin Wang }
73*5fd752b6SBaolin Wang 
74*5fd752b6SBaolin Wang static void pattern_trig_timer_function(struct timer_list *t)
75*5fd752b6SBaolin Wang {
76*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = from_timer(data, t, timer);
77*5fd752b6SBaolin Wang 
78*5fd752b6SBaolin Wang 	mutex_lock(&data->lock);
79*5fd752b6SBaolin Wang 
80*5fd752b6SBaolin Wang 	for (;;) {
81*5fd752b6SBaolin Wang 		if (!data->is_indefinite && !data->repeat)
82*5fd752b6SBaolin Wang 			break;
83*5fd752b6SBaolin Wang 
84*5fd752b6SBaolin Wang 		if (data->curr->brightness == data->next->brightness) {
85*5fd752b6SBaolin Wang 			/* Step change of brightness */
86*5fd752b6SBaolin Wang 			led_set_brightness(data->led_cdev,
87*5fd752b6SBaolin Wang 					   data->curr->brightness);
88*5fd752b6SBaolin Wang 			mod_timer(&data->timer,
89*5fd752b6SBaolin Wang 				  jiffies + msecs_to_jiffies(data->curr->delta_t));
90*5fd752b6SBaolin Wang 
91*5fd752b6SBaolin Wang 			/* Skip the tuple with zero duration */
92*5fd752b6SBaolin Wang 			pattern_trig_update_patterns(data);
93*5fd752b6SBaolin Wang 			/* Select next tuple */
94*5fd752b6SBaolin Wang 			pattern_trig_update_patterns(data);
95*5fd752b6SBaolin Wang 		} else {
96*5fd752b6SBaolin Wang 			/* Gradual dimming */
97*5fd752b6SBaolin Wang 
98*5fd752b6SBaolin Wang 			/*
99*5fd752b6SBaolin Wang 			 * If the accumulation time is larger than current
100*5fd752b6SBaolin Wang 			 * tuple's duration, we should go next one and re-check
101*5fd752b6SBaolin Wang 			 * if we repeated done.
102*5fd752b6SBaolin Wang 			 */
103*5fd752b6SBaolin Wang 			if (data->delta_t > data->curr->delta_t) {
104*5fd752b6SBaolin Wang 				pattern_trig_update_patterns(data);
105*5fd752b6SBaolin Wang 				continue;
106*5fd752b6SBaolin Wang 			}
107*5fd752b6SBaolin Wang 
108*5fd752b6SBaolin Wang 			led_set_brightness(data->led_cdev,
109*5fd752b6SBaolin Wang 					   pattern_trig_compute_brightness(data));
110*5fd752b6SBaolin Wang 			mod_timer(&data->timer,
111*5fd752b6SBaolin Wang 				  jiffies + msecs_to_jiffies(UPDATE_INTERVAL));
112*5fd752b6SBaolin Wang 
113*5fd752b6SBaolin Wang 			/* Accumulate the gradual dimming time */
114*5fd752b6SBaolin Wang 			data->delta_t += UPDATE_INTERVAL;
115*5fd752b6SBaolin Wang 		}
116*5fd752b6SBaolin Wang 
117*5fd752b6SBaolin Wang 		break;
118*5fd752b6SBaolin Wang 	}
119*5fd752b6SBaolin Wang 
120*5fd752b6SBaolin Wang 	mutex_unlock(&data->lock);
121*5fd752b6SBaolin Wang }
122*5fd752b6SBaolin Wang 
123*5fd752b6SBaolin Wang static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
124*5fd752b6SBaolin Wang {
125*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = led_cdev->trigger_data;
126*5fd752b6SBaolin Wang 
127*5fd752b6SBaolin Wang 	if (!data->npatterns)
128*5fd752b6SBaolin Wang 		return 0;
129*5fd752b6SBaolin Wang 
130*5fd752b6SBaolin Wang 	if (data->is_hw_pattern) {
131*5fd752b6SBaolin Wang 		return led_cdev->pattern_set(led_cdev, data->patterns,
132*5fd752b6SBaolin Wang 					     data->npatterns, data->repeat);
133*5fd752b6SBaolin Wang 	}
134*5fd752b6SBaolin Wang 
135*5fd752b6SBaolin Wang 	/* At least 2 tuples for software pattern. */
136*5fd752b6SBaolin Wang 	if (data->npatterns < 2)
137*5fd752b6SBaolin Wang 		return -EINVAL;
138*5fd752b6SBaolin Wang 
139*5fd752b6SBaolin Wang 	data->delta_t = 0;
140*5fd752b6SBaolin Wang 	data->curr = data->patterns;
141*5fd752b6SBaolin Wang 	data->next = data->patterns + 1;
142*5fd752b6SBaolin Wang 	data->timer.expires = jiffies;
143*5fd752b6SBaolin Wang 	add_timer(&data->timer);
144*5fd752b6SBaolin Wang 
145*5fd752b6SBaolin Wang 	return 0;
146*5fd752b6SBaolin Wang }
147*5fd752b6SBaolin Wang 
148*5fd752b6SBaolin Wang static ssize_t repeat_show(struct device *dev, struct device_attribute *attr,
149*5fd752b6SBaolin Wang 			   char *buf)
150*5fd752b6SBaolin Wang {
151*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
152*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = led_cdev->trigger_data;
153*5fd752b6SBaolin Wang 	int repeat;
154*5fd752b6SBaolin Wang 
155*5fd752b6SBaolin Wang 	mutex_lock(&data->lock);
156*5fd752b6SBaolin Wang 
157*5fd752b6SBaolin Wang 	repeat = data->last_repeat;
158*5fd752b6SBaolin Wang 
159*5fd752b6SBaolin Wang 	mutex_unlock(&data->lock);
160*5fd752b6SBaolin Wang 
161*5fd752b6SBaolin Wang 	return scnprintf(buf, PAGE_SIZE, "%d\n", repeat);
162*5fd752b6SBaolin Wang }
163*5fd752b6SBaolin Wang 
164*5fd752b6SBaolin Wang static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
165*5fd752b6SBaolin Wang 			    const char *buf, size_t count)
166*5fd752b6SBaolin Wang {
167*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
168*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = led_cdev->trigger_data;
169*5fd752b6SBaolin Wang 	int err, res;
170*5fd752b6SBaolin Wang 
171*5fd752b6SBaolin Wang 	err = kstrtos32(buf, 10, &res);
172*5fd752b6SBaolin Wang 	if (err)
173*5fd752b6SBaolin Wang 		return err;
174*5fd752b6SBaolin Wang 
175*5fd752b6SBaolin Wang 	/* Number 0 and negative numbers except -1 are invalid. */
176*5fd752b6SBaolin Wang 	if (res < -1 || res == 0)
177*5fd752b6SBaolin Wang 		return -EINVAL;
178*5fd752b6SBaolin Wang 
179*5fd752b6SBaolin Wang 	/*
180*5fd752b6SBaolin Wang 	 * Clear previous patterns' performence firstly, and remove the timer
181*5fd752b6SBaolin Wang 	 * without mutex lock to avoid dead lock.
182*5fd752b6SBaolin Wang 	 */
183*5fd752b6SBaolin Wang 	del_timer_sync(&data->timer);
184*5fd752b6SBaolin Wang 
185*5fd752b6SBaolin Wang 	mutex_lock(&data->lock);
186*5fd752b6SBaolin Wang 
187*5fd752b6SBaolin Wang 	if (data->is_hw_pattern)
188*5fd752b6SBaolin Wang 		led_cdev->pattern_clear(led_cdev);
189*5fd752b6SBaolin Wang 
190*5fd752b6SBaolin Wang 	data->last_repeat = data->repeat = res;
191*5fd752b6SBaolin Wang 	/* -1 means repeat indefinitely */
192*5fd752b6SBaolin Wang 	if (data->repeat == -1)
193*5fd752b6SBaolin Wang 		data->is_indefinite = true;
194*5fd752b6SBaolin Wang 	else
195*5fd752b6SBaolin Wang 		data->is_indefinite = false;
196*5fd752b6SBaolin Wang 
197*5fd752b6SBaolin Wang 	err = pattern_trig_start_pattern(led_cdev);
198*5fd752b6SBaolin Wang 
199*5fd752b6SBaolin Wang 	mutex_unlock(&data->lock);
200*5fd752b6SBaolin Wang 	return err < 0 ? err : count;
201*5fd752b6SBaolin Wang }
202*5fd752b6SBaolin Wang 
203*5fd752b6SBaolin Wang static DEVICE_ATTR_RW(repeat);
204*5fd752b6SBaolin Wang 
205*5fd752b6SBaolin Wang static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
206*5fd752b6SBaolin Wang 					  char *buf, bool hw_pattern)
207*5fd752b6SBaolin Wang {
208*5fd752b6SBaolin Wang 	ssize_t count = 0;
209*5fd752b6SBaolin Wang 	int i;
210*5fd752b6SBaolin Wang 
211*5fd752b6SBaolin Wang 	mutex_lock(&data->lock);
212*5fd752b6SBaolin Wang 
213*5fd752b6SBaolin Wang 	if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern))
214*5fd752b6SBaolin Wang 		goto out;
215*5fd752b6SBaolin Wang 
216*5fd752b6SBaolin Wang 	for (i = 0; i < data->npatterns; i++) {
217*5fd752b6SBaolin Wang 		count += scnprintf(buf + count, PAGE_SIZE - count,
218*5fd752b6SBaolin Wang 				   "%d %u ",
219*5fd752b6SBaolin Wang 				   data->patterns[i].brightness,
220*5fd752b6SBaolin Wang 				   data->patterns[i].delta_t);
221*5fd752b6SBaolin Wang 	}
222*5fd752b6SBaolin Wang 
223*5fd752b6SBaolin Wang 	buf[count - 1] = '\n';
224*5fd752b6SBaolin Wang 
225*5fd752b6SBaolin Wang out:
226*5fd752b6SBaolin Wang 	mutex_unlock(&data->lock);
227*5fd752b6SBaolin Wang 	return count;
228*5fd752b6SBaolin Wang }
229*5fd752b6SBaolin Wang 
230*5fd752b6SBaolin Wang static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
231*5fd752b6SBaolin Wang 					   const char *buf, size_t count,
232*5fd752b6SBaolin Wang 					   bool hw_pattern)
233*5fd752b6SBaolin Wang {
234*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = led_cdev->trigger_data;
235*5fd752b6SBaolin Wang 	int ccount, cr, offset = 0, err = 0;
236*5fd752b6SBaolin Wang 
237*5fd752b6SBaolin Wang 	/*
238*5fd752b6SBaolin Wang 	 * Clear previous patterns' performence firstly, and remove the timer
239*5fd752b6SBaolin Wang 	 * without mutex lock to avoid dead lock.
240*5fd752b6SBaolin Wang 	 */
241*5fd752b6SBaolin Wang 	del_timer_sync(&data->timer);
242*5fd752b6SBaolin Wang 
243*5fd752b6SBaolin Wang 	mutex_lock(&data->lock);
244*5fd752b6SBaolin Wang 
245*5fd752b6SBaolin Wang 	if (data->is_hw_pattern)
246*5fd752b6SBaolin Wang 		led_cdev->pattern_clear(led_cdev);
247*5fd752b6SBaolin Wang 
248*5fd752b6SBaolin Wang 	data->is_hw_pattern = hw_pattern;
249*5fd752b6SBaolin Wang 	data->npatterns = 0;
250*5fd752b6SBaolin Wang 
251*5fd752b6SBaolin Wang 	while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
252*5fd752b6SBaolin Wang 		cr = 0;
253*5fd752b6SBaolin Wang 		ccount = sscanf(buf + offset, "%d %u %n",
254*5fd752b6SBaolin Wang 				&data->patterns[data->npatterns].brightness,
255*5fd752b6SBaolin Wang 				&data->patterns[data->npatterns].delta_t, &cr);
256*5fd752b6SBaolin Wang 		if (ccount != 2) {
257*5fd752b6SBaolin Wang 			data->npatterns = 0;
258*5fd752b6SBaolin Wang 			err = -EINVAL;
259*5fd752b6SBaolin Wang 			goto out;
260*5fd752b6SBaolin Wang 		}
261*5fd752b6SBaolin Wang 
262*5fd752b6SBaolin Wang 		offset += cr;
263*5fd752b6SBaolin Wang 		data->npatterns++;
264*5fd752b6SBaolin Wang 	}
265*5fd752b6SBaolin Wang 
266*5fd752b6SBaolin Wang 	err = pattern_trig_start_pattern(led_cdev);
267*5fd752b6SBaolin Wang 	if (err)
268*5fd752b6SBaolin Wang 		data->npatterns = 0;
269*5fd752b6SBaolin Wang 
270*5fd752b6SBaolin Wang out:
271*5fd752b6SBaolin Wang 	mutex_unlock(&data->lock);
272*5fd752b6SBaolin Wang 	return err < 0 ? err : count;
273*5fd752b6SBaolin Wang }
274*5fd752b6SBaolin Wang 
275*5fd752b6SBaolin Wang static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
276*5fd752b6SBaolin Wang 			    char *buf)
277*5fd752b6SBaolin Wang {
278*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
279*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = led_cdev->trigger_data;
280*5fd752b6SBaolin Wang 
281*5fd752b6SBaolin Wang 	return pattern_trig_show_patterns(data, buf, false);
282*5fd752b6SBaolin Wang }
283*5fd752b6SBaolin Wang 
284*5fd752b6SBaolin Wang static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
285*5fd752b6SBaolin Wang 			     const char *buf, size_t count)
286*5fd752b6SBaolin Wang {
287*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
288*5fd752b6SBaolin Wang 
289*5fd752b6SBaolin Wang 	return pattern_trig_store_patterns(led_cdev, buf, count, false);
290*5fd752b6SBaolin Wang }
291*5fd752b6SBaolin Wang 
292*5fd752b6SBaolin Wang static DEVICE_ATTR_RW(pattern);
293*5fd752b6SBaolin Wang 
294*5fd752b6SBaolin Wang static ssize_t hw_pattern_show(struct device *dev,
295*5fd752b6SBaolin Wang 			       struct device_attribute *attr, char *buf)
296*5fd752b6SBaolin Wang {
297*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
298*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = led_cdev->trigger_data;
299*5fd752b6SBaolin Wang 
300*5fd752b6SBaolin Wang 	return pattern_trig_show_patterns(data, buf, true);
301*5fd752b6SBaolin Wang }
302*5fd752b6SBaolin Wang 
303*5fd752b6SBaolin Wang static ssize_t hw_pattern_store(struct device *dev,
304*5fd752b6SBaolin Wang 				struct device_attribute *attr,
305*5fd752b6SBaolin Wang 				const char *buf, size_t count)
306*5fd752b6SBaolin Wang {
307*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
308*5fd752b6SBaolin Wang 
309*5fd752b6SBaolin Wang 	return pattern_trig_store_patterns(led_cdev, buf, count, true);
310*5fd752b6SBaolin Wang }
311*5fd752b6SBaolin Wang 
312*5fd752b6SBaolin Wang static DEVICE_ATTR_RW(hw_pattern);
313*5fd752b6SBaolin Wang 
314*5fd752b6SBaolin Wang static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
315*5fd752b6SBaolin Wang 				       struct attribute *attr, int index)
316*5fd752b6SBaolin Wang {
317*5fd752b6SBaolin Wang 	struct device *dev = container_of(kobj, struct device, kobj);
318*5fd752b6SBaolin Wang 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
319*5fd752b6SBaolin Wang 
320*5fd752b6SBaolin Wang 	if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
321*5fd752b6SBaolin Wang 		return attr->mode;
322*5fd752b6SBaolin Wang 	else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
323*5fd752b6SBaolin Wang 		return attr->mode;
324*5fd752b6SBaolin Wang 
325*5fd752b6SBaolin Wang 	return 0;
326*5fd752b6SBaolin Wang }
327*5fd752b6SBaolin Wang 
328*5fd752b6SBaolin Wang static struct attribute *pattern_trig_attrs[] = {
329*5fd752b6SBaolin Wang 	&dev_attr_pattern.attr,
330*5fd752b6SBaolin Wang 	&dev_attr_hw_pattern.attr,
331*5fd752b6SBaolin Wang 	&dev_attr_repeat.attr,
332*5fd752b6SBaolin Wang 	NULL
333*5fd752b6SBaolin Wang };
334*5fd752b6SBaolin Wang 
335*5fd752b6SBaolin Wang static const struct attribute_group pattern_trig_group = {
336*5fd752b6SBaolin Wang 	.attrs = pattern_trig_attrs,
337*5fd752b6SBaolin Wang 	.is_visible = pattern_trig_attrs_mode,
338*5fd752b6SBaolin Wang };
339*5fd752b6SBaolin Wang 
340*5fd752b6SBaolin Wang static const struct attribute_group *pattern_trig_groups[] = {
341*5fd752b6SBaolin Wang 	&pattern_trig_group,
342*5fd752b6SBaolin Wang 	NULL,
343*5fd752b6SBaolin Wang };
344*5fd752b6SBaolin Wang 
345*5fd752b6SBaolin Wang static int pattern_trig_activate(struct led_classdev *led_cdev)
346*5fd752b6SBaolin Wang {
347*5fd752b6SBaolin Wang 	struct pattern_trig_data *data;
348*5fd752b6SBaolin Wang 
349*5fd752b6SBaolin Wang 	data = kzalloc(sizeof(*data), GFP_KERNEL);
350*5fd752b6SBaolin Wang 	if (!data)
351*5fd752b6SBaolin Wang 		return -ENOMEM;
352*5fd752b6SBaolin Wang 
353*5fd752b6SBaolin Wang 	if (!!led_cdev->pattern_set ^ !!led_cdev->pattern_clear) {
354*5fd752b6SBaolin Wang 		dev_warn(led_cdev->dev,
355*5fd752b6SBaolin Wang 			 "Hardware pattern ops validation failed\n");
356*5fd752b6SBaolin Wang 		led_cdev->pattern_set = NULL;
357*5fd752b6SBaolin Wang 		led_cdev->pattern_clear = NULL;
358*5fd752b6SBaolin Wang 	}
359*5fd752b6SBaolin Wang 
360*5fd752b6SBaolin Wang 	data->is_indefinite = true;
361*5fd752b6SBaolin Wang 	data->last_repeat = -1;
362*5fd752b6SBaolin Wang 	mutex_init(&data->lock);
363*5fd752b6SBaolin Wang 	data->led_cdev = led_cdev;
364*5fd752b6SBaolin Wang 	led_set_trigger_data(led_cdev, data);
365*5fd752b6SBaolin Wang 	timer_setup(&data->timer, pattern_trig_timer_function, 0);
366*5fd752b6SBaolin Wang 	led_cdev->activated = true;
367*5fd752b6SBaolin Wang 
368*5fd752b6SBaolin Wang 	return 0;
369*5fd752b6SBaolin Wang }
370*5fd752b6SBaolin Wang 
371*5fd752b6SBaolin Wang static void pattern_trig_deactivate(struct led_classdev *led_cdev)
372*5fd752b6SBaolin Wang {
373*5fd752b6SBaolin Wang 	struct pattern_trig_data *data = led_cdev->trigger_data;
374*5fd752b6SBaolin Wang 
375*5fd752b6SBaolin Wang 	if (!led_cdev->activated)
376*5fd752b6SBaolin Wang 		return;
377*5fd752b6SBaolin Wang 
378*5fd752b6SBaolin Wang 	if (led_cdev->pattern_clear)
379*5fd752b6SBaolin Wang 		led_cdev->pattern_clear(led_cdev);
380*5fd752b6SBaolin Wang 
381*5fd752b6SBaolin Wang 	del_timer_sync(&data->timer);
382*5fd752b6SBaolin Wang 
383*5fd752b6SBaolin Wang 	led_set_brightness(led_cdev, LED_OFF);
384*5fd752b6SBaolin Wang 	kfree(data);
385*5fd752b6SBaolin Wang 	led_cdev->activated = false;
386*5fd752b6SBaolin Wang }
387*5fd752b6SBaolin Wang 
388*5fd752b6SBaolin Wang static struct led_trigger pattern_led_trigger = {
389*5fd752b6SBaolin Wang 	.name = "pattern",
390*5fd752b6SBaolin Wang 	.activate = pattern_trig_activate,
391*5fd752b6SBaolin Wang 	.deactivate = pattern_trig_deactivate,
392*5fd752b6SBaolin Wang 	.groups = pattern_trig_groups,
393*5fd752b6SBaolin Wang };
394*5fd752b6SBaolin Wang 
395*5fd752b6SBaolin Wang static int __init pattern_trig_init(void)
396*5fd752b6SBaolin Wang {
397*5fd752b6SBaolin Wang 	return led_trigger_register(&pattern_led_trigger);
398*5fd752b6SBaolin Wang }
399*5fd752b6SBaolin Wang 
400*5fd752b6SBaolin Wang static void __exit pattern_trig_exit(void)
401*5fd752b6SBaolin Wang {
402*5fd752b6SBaolin Wang 	led_trigger_unregister(&pattern_led_trigger);
403*5fd752b6SBaolin Wang }
404*5fd752b6SBaolin Wang 
405*5fd752b6SBaolin Wang module_init(pattern_trig_init);
406*5fd752b6SBaolin Wang module_exit(pattern_trig_exit);
407*5fd752b6SBaolin Wang 
408*5fd752b6SBaolin Wang MODULE_AUTHOR("Raphael Teysseyre <[email protected]");
409*5fd752b6SBaolin Wang MODULE_AUTHOR("Baolin Wang <[email protected]");
410*5fd752b6SBaolin Wang MODULE_DESCRIPTION("LED Pattern trigger");
411*5fd752b6SBaolin Wang MODULE_LICENSE("GPL v2");
412