Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0
  2
  3/*
  4 * LED pattern trigger
  5 *
  6 * Idea discussed with Pavel Machek. Raphael Teysseyre implemented
  7 * the first version, Baolin Wang simplified and improved the approach.
  8 */
  9
 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
 17#define MAX_PATTERNS		1024
 18/*
 19 * When doing gradual dimming, the led brightness will be updated
 20 * every 50 milliseconds.
 21 */
 22#define UPDATE_INTERVAL		50
 23
 
 
 
 
 
 
 24struct 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;
 
 37};
 38
 39static void pattern_trig_update_patterns(struct pattern_trig_data *data)
 40{
 41	data->curr = data->next;
 42	if (!data->is_indefinite && data->curr == data->patterns)
 43		data->repeat--;
 44
 45	if (data->next == data->patterns + data->npatterns - 1)
 46		data->next = data->patterns;
 47	else
 48		data->next++;
 49
 50	data->delta_t = 0;
 51}
 52
 53static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
 54{
 55	int step_brightness;
 56
 57	/*
 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.
 61	 */
 62	if (data->delta_t == 0 || data->curr->delta_t < UPDATE_INTERVAL)
 63		return data->curr->brightness;
 64
 65	step_brightness = abs(data->next->brightness - data->curr->brightness);
 66	step_brightness = data->delta_t * step_brightness / data->curr->delta_t;
 67
 68	if (data->next->brightness > data->curr->brightness)
 69		return data->curr->brightness + step_brightness;
 70	else
 71		return data->curr->brightness - step_brightness;
 72}
 73
 74static void pattern_trig_timer_function(struct timer_list *t)
 75{
 76	struct pattern_trig_data *data = from_timer(data, t, timer);
 
 
 
 
 
 
 77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 78	for (;;) {
 79		if (!data->is_indefinite && !data->repeat)
 80			break;
 81
 82		if (data->curr->brightness == data->next->brightness) {
 83			/* Step change of brightness */
 84			led_set_brightness(data->led_cdev,
 85					   data->curr->brightness);
 86			mod_timer(&data->timer,
 87				  jiffies + msecs_to_jiffies(data->curr->delta_t));
 88			if (!data->next->delta_t) {
 89				/* Skip the tuple with zero duration */
 90				pattern_trig_update_patterns(data);
 91			}
 92			/* Select next tuple */
 93			pattern_trig_update_patterns(data);
 94		} else {
 95			/* Gradual dimming */
 96
 97			/*
 98			 * If the accumulation time is larger than current
 99			 * tuple's duration, we should go next one and re-check
100			 * if we repeated done.
101			 */
102			if (data->delta_t > data->curr->delta_t) {
103				pattern_trig_update_patterns(data);
104				continue;
105			}
106
107			led_set_brightness(data->led_cdev,
108					   pattern_trig_compute_brightness(data));
109			mod_timer(&data->timer,
110				  jiffies + msecs_to_jiffies(UPDATE_INTERVAL));
111
112			/* Accumulate the gradual dimming time */
113			data->delta_t += UPDATE_INTERVAL;
114		}
115
116		break;
117	}
118}
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
121{
122	struct pattern_trig_data *data = led_cdev->trigger_data;
123
124	if (!data->npatterns)
125		return 0;
126
127	if (data->is_hw_pattern) {
128		return led_cdev->pattern_set(led_cdev, data->patterns,
129					     data->npatterns, data->repeat);
130	}
131
132	/* At least 2 tuples for software pattern. */
133	if (data->npatterns < 2)
134		return -EINVAL;
135
136	data->delta_t = 0;
137	data->curr = data->patterns;
138	data->next = data->patterns + 1;
139	data->timer.expires = jiffies;
140	add_timer(&data->timer);
141
142	return 0;
143}
144
145static ssize_t repeat_show(struct device *dev, struct device_attribute *attr,
146			   char *buf)
147{
148	struct led_classdev *led_cdev = dev_get_drvdata(dev);
149	struct pattern_trig_data *data = led_cdev->trigger_data;
150	int repeat;
151
152	mutex_lock(&data->lock);
153
154	repeat = data->last_repeat;
155
156	mutex_unlock(&data->lock);
157
158	return sysfs_emit(buf, "%d\n", repeat);
159}
160
161static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
162			    const char *buf, size_t count)
163{
164	struct led_classdev *led_cdev = dev_get_drvdata(dev);
165	struct pattern_trig_data *data = led_cdev->trigger_data;
166	int err, res;
167
168	err = kstrtos32(buf, 10, &res);
169	if (err)
170		return err;
171
172	/* Number 0 and negative numbers except -1 are invalid. */
173	if (res < -1 || res == 0)
174		return -EINVAL;
175
176	mutex_lock(&data->lock);
177
178	del_timer_sync(&data->timer);
179
180	if (data->is_hw_pattern)
181		led_cdev->pattern_clear(led_cdev);
182
183	data->last_repeat = data->repeat = res;
184	/* -1 means repeat indefinitely */
185	if (data->repeat == -1)
186		data->is_indefinite = true;
187	else
188		data->is_indefinite = false;
189
190	err = pattern_trig_start_pattern(led_cdev);
191
192	mutex_unlock(&data->lock);
193	return err < 0 ? err : count;
194}
195
196static DEVICE_ATTR_RW(repeat);
197
198static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
199					  char *buf, bool hw_pattern)
200{
201	ssize_t count = 0;
202	int i;
203
204	mutex_lock(&data->lock);
205
206	if (!data->npatterns || (data->is_hw_pattern ^ hw_pattern))
207		goto out;
208
209	for (i = 0; i < data->npatterns; i++) {
210		count += scnprintf(buf + count, PAGE_SIZE - count,
211				   "%d %u ",
212				   data->patterns[i].brightness,
213				   data->patterns[i].delta_t);
214	}
215
216	buf[count - 1] = '\n';
217
218out:
219	mutex_unlock(&data->lock);
220	return count;
221}
222
223static int pattern_trig_store_patterns_string(struct pattern_trig_data *data,
224					      const char *buf, size_t count)
225{
226	int ccount, cr, offset = 0;
227
228	while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
229		cr = 0;
230		ccount = sscanf(buf + offset, "%u %u %n",
231				&data->patterns[data->npatterns].brightness,
232				&data->patterns[data->npatterns].delta_t, &cr);
233
234		if (ccount != 2 ||
235		    data->patterns[data->npatterns].brightness > data->led_cdev->max_brightness) {
236			data->npatterns = 0;
237			return -EINVAL;
238		}
239
240		offset += cr;
241		data->npatterns++;
242	}
243
244	return 0;
245}
246
247static int pattern_trig_store_patterns_int(struct pattern_trig_data *data,
248					   const u32 *buf, size_t count)
249{
250	unsigned int i;
251
252	for (i = 0; i < count; i += 2) {
253		data->patterns[data->npatterns].brightness = buf[i];
254		data->patterns[data->npatterns].delta_t = buf[i + 1];
255		data->npatterns++;
256	}
257
258	return 0;
259}
260
261static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
262					   const char *buf, const u32 *buf_int,
263					   size_t count, bool hw_pattern)
264{
265	struct pattern_trig_data *data = led_cdev->trigger_data;
266	int err = 0;
267
268	mutex_lock(&data->lock);
269
270	del_timer_sync(&data->timer);
271
272	if (data->is_hw_pattern)
273		led_cdev->pattern_clear(led_cdev);
274
275	data->is_hw_pattern = hw_pattern;
276	data->npatterns = 0;
277
278	if (buf)
279		err = pattern_trig_store_patterns_string(data, buf, count);
280	else
281		err = pattern_trig_store_patterns_int(data, buf_int, count);
282	if (err)
283		goto out;
284
285	err = pattern_trig_start_pattern(led_cdev);
286	if (err)
287		data->npatterns = 0;
288
289out:
290	mutex_unlock(&data->lock);
291	return err < 0 ? err : count;
292}
293
294static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
295			    char *buf)
296{
297	struct led_classdev *led_cdev = dev_get_drvdata(dev);
298	struct pattern_trig_data *data = led_cdev->trigger_data;
299
300	return pattern_trig_show_patterns(data, buf, false);
301}
302
303static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
304			     const char *buf, size_t count)
305{
306	struct led_classdev *led_cdev = dev_get_drvdata(dev);
307
308	return pattern_trig_store_patterns(led_cdev, buf, NULL, count, false);
 
309}
310
311static DEVICE_ATTR_RW(pattern);
312
313static ssize_t hw_pattern_show(struct device *dev,
314			       struct device_attribute *attr, char *buf)
315{
316	struct led_classdev *led_cdev = dev_get_drvdata(dev);
317	struct pattern_trig_data *data = led_cdev->trigger_data;
318
319	return pattern_trig_show_patterns(data, buf, true);
320}
321
322static ssize_t hw_pattern_store(struct device *dev,
323				struct device_attribute *attr,
324				const char *buf, size_t count)
325{
326	struct led_classdev *led_cdev = dev_get_drvdata(dev);
327
328	return pattern_trig_store_patterns(led_cdev, buf, NULL, count, true);
 
329}
330
331static DEVICE_ATTR_RW(hw_pattern);
332
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
334				       struct attribute *attr, int index)
335{
336	struct device *dev = kobj_to_dev(kobj);
337	struct led_classdev *led_cdev = dev_get_drvdata(dev);
338
339	if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
340		return attr->mode;
 
 
341	else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
342		return attr->mode;
343
344	return 0;
345}
346
347static struct attribute *pattern_trig_attrs[] = {
348	&dev_attr_pattern.attr,
349	&dev_attr_hw_pattern.attr,
 
350	&dev_attr_repeat.attr,
351	NULL
352};
353
354static const struct attribute_group pattern_trig_group = {
355	.attrs = pattern_trig_attrs,
356	.is_visible = pattern_trig_attrs_mode,
357};
358
359static const struct attribute_group *pattern_trig_groups[] = {
360	&pattern_trig_group,
361	NULL,
362};
363
364static void pattern_init(struct led_classdev *led_cdev)
365{
366	unsigned int size = 0;
367	u32 *pattern;
368	int err;
369
370	pattern = led_get_default_pattern(led_cdev, &size);
371	if (!pattern)
372		return;
373
374	if (size % 2) {
375		dev_warn(led_cdev->dev, "Expected pattern of tuples\n");
376		goto out;
377	}
378
379	err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size, false);
 
380	if (err < 0)
381		dev_warn(led_cdev->dev,
382			 "Pattern initialization failed with error %d\n", err);
383
384out:
385	kfree(pattern);
386}
387
388static int pattern_trig_activate(struct led_classdev *led_cdev)
389{
390	struct pattern_trig_data *data;
391
392	data = kzalloc(sizeof(*data), GFP_KERNEL);
393	if (!data)
394		return -ENOMEM;
395
396	if (!!led_cdev->pattern_set ^ !!led_cdev->pattern_clear) {
397		dev_warn(led_cdev->dev,
398			 "Hardware pattern ops validation failed\n");
399		led_cdev->pattern_set = NULL;
400		led_cdev->pattern_clear = NULL;
401	}
402
 
403	data->is_indefinite = true;
404	data->last_repeat = -1;
405	mutex_init(&data->lock);
406	data->led_cdev = led_cdev;
407	led_set_trigger_data(led_cdev, data);
408	timer_setup(&data->timer, pattern_trig_timer_function, 0);
 
 
409	led_cdev->activated = true;
410
411	if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
412		pattern_init(led_cdev);
413		/*
414		 * Mark as initialized even on pattern_init() error because
415		 * any consecutive call to it would produce the same error.
416		 */
417		led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
418	}
419
420	return 0;
421}
422
423static void pattern_trig_deactivate(struct led_classdev *led_cdev)
424{
425	struct pattern_trig_data *data = led_cdev->trigger_data;
426
427	if (!led_cdev->activated)
428		return;
429
430	if (led_cdev->pattern_clear)
431		led_cdev->pattern_clear(led_cdev);
432
433	timer_shutdown_sync(&data->timer);
 
434
435	led_set_brightness(led_cdev, LED_OFF);
436	kfree(data);
437	led_cdev->activated = false;
438}
439
440static struct led_trigger pattern_led_trigger = {
441	.name = "pattern",
442	.activate = pattern_trig_activate,
443	.deactivate = pattern_trig_deactivate,
444	.groups = pattern_trig_groups,
445};
446
447static int __init pattern_trig_init(void)
448{
449	return led_trigger_register(&pattern_led_trigger);
450}
451
452static void __exit pattern_trig_exit(void)
453{
454	led_trigger_unregister(&pattern_led_trigger);
455}
456
457module_init(pattern_trig_init);
458module_exit(pattern_trig_exit);
459
460MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com>");
461MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
462MODULE_DESCRIPTION("LED Pattern trigger");
463MODULE_LICENSE("GPL v2");
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2
  3/*
  4 * LED pattern trigger
  5 *
  6 * Idea discussed with Pavel Machek. Raphael Teysseyre implemented
  7 * the first version, Baolin Wang simplified and improved the approach.
  8 */
  9
 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>
 17
 18#define MAX_PATTERNS		1024
 19/*
 20 * When doing gradual dimming, the led brightness will be updated
 21 * every 50 milliseconds.
 22 */
 23#define UPDATE_INTERVAL		50
 24
 25enum pattern_type {
 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 */
 29};
 30
 31struct 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;
 36	struct mutex lock;
 37	u32 npatterns;
 38	int repeat;
 39	int last_repeat;
 40	int delta_t;
 41	bool is_indefinite;
 42	enum pattern_type type;
 43	struct timer_list timer;
 44	struct hrtimer hrtimer;
 45};
 46
 47static void pattern_trig_update_patterns(struct pattern_trig_data *data)
 48{
 49	data->curr = data->next;
 50	if (!data->is_indefinite && data->curr == data->patterns)
 51		data->repeat--;
 52
 53	if (data->next == data->patterns + data->npatterns - 1)
 54		data->next = data->patterns;
 55	else
 56		data->next++;
 57
 58	data->delta_t = 0;
 59}
 60
 61static int pattern_trig_compute_brightness(struct pattern_trig_data *data)
 62{
 63	int step_brightness;
 64
 65	/*
 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.
 69	 */
 70	if (data->delta_t == 0 || data->curr->delta_t < UPDATE_INTERVAL)
 71		return data->curr->brightness;
 72
 73	step_brightness = abs(data->next->brightness - data->curr->brightness);
 74	step_brightness = data->delta_t * step_brightness / data->curr->delta_t;
 75
 76	if (data->next->brightness > data->curr->brightness)
 77		return data->curr->brightness + step_brightness;
 78	else
 79		return data->curr->brightness - step_brightness;
 80}
 81
 82static void pattern_trig_timer_start(struct pattern_trig_data *data)
 83{
 84	if (data->type == PATTERN_TYPE_HR) {
 85		hrtimer_start(&data->hrtimer, ns_to_ktime(0), HRTIMER_MODE_REL);
 86	} else {
 87		data->timer.expires = jiffies;
 88		add_timer(&data->timer);
 89	}
 90}
 91
 92static void pattern_trig_timer_cancel(struct pattern_trig_data *data)
 93{
 94	if (data->type == PATTERN_TYPE_HR)
 95		hrtimer_cancel(&data->hrtimer);
 96	else
 97		del_timer_sync(&data->timer);
 98}
 99
100static void pattern_trig_timer_restart(struct pattern_trig_data *data,
101				       unsigned long interval)
102{
103	if (data->type == PATTERN_TYPE_HR)
104		hrtimer_forward_now(&data->hrtimer, ms_to_ktime(interval));
105	else
106		mod_timer(&data->timer, jiffies + msecs_to_jiffies(interval));
107}
108
109static void pattern_trig_timer_common_function(struct pattern_trig_data *data)
110{
111	for (;;) {
112		if (!data->is_indefinite && !data->repeat)
113			break;
114
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);
123			}
124			/* Select next tuple */
125			pattern_trig_update_patterns(data);
126		} else {
127			/* Gradual dimming */
128
129			/*
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.
133			 */
134			if (data->delta_t > data->curr->delta_t) {
135				pattern_trig_update_patterns(data);
136				continue;
137			}
138
139			led_set_brightness(data->led_cdev,
140					   pattern_trig_compute_brightness(data));
141			pattern_trig_timer_restart(data, UPDATE_INTERVAL);
 
142
143			/* Accumulate the gradual dimming time */
144			data->delta_t += UPDATE_INTERVAL;
145		}
146
147		break;
148	}
149}
150
151static void pattern_trig_timer_function(struct timer_list *t)
152{
153	struct pattern_trig_data *data = from_timer(data, t, timer);
154
155	return pattern_trig_timer_common_function(data);
156}
157
158static enum hrtimer_restart pattern_trig_hrtimer_function(struct hrtimer *t)
159{
160	struct pattern_trig_data *data =
161		container_of(t, struct pattern_trig_data, hrtimer);
162
163	pattern_trig_timer_common_function(data);
164	if (!data->is_indefinite && !data->repeat)
165		return HRTIMER_NORESTART;
166
167	return HRTIMER_RESTART;
168}
169
170static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
171{
172	struct pattern_trig_data *data = led_cdev->trigger_data;
173
174	if (!data->npatterns)
175		return 0;
176
177	if (data->type == PATTERN_TYPE_HW) {
178		return led_cdev->pattern_set(led_cdev, data->patterns,
179					     data->npatterns, data->repeat);
180	}
181
182	/* At least 2 tuples for software pattern. */
183	if (data->npatterns < 2)
184		return -EINVAL;
185
186	data->delta_t = 0;
187	data->curr = data->patterns;
188	data->next = data->patterns + 1;
189	pattern_trig_timer_start(data);
 
190
191	return 0;
192}
193
194static ssize_t repeat_show(struct device *dev, struct device_attribute *attr,
195			   char *buf)
196{
197	struct led_classdev *led_cdev = dev_get_drvdata(dev);
198	struct pattern_trig_data *data = led_cdev->trigger_data;
199	int repeat;
200
201	mutex_lock(&data->lock);
202
203	repeat = data->last_repeat;
204
205	mutex_unlock(&data->lock);
206
207	return sysfs_emit(buf, "%d\n", repeat);
208}
209
210static ssize_t repeat_store(struct device *dev, struct device_attribute *attr,
211			    const char *buf, size_t count)
212{
213	struct led_classdev *led_cdev = dev_get_drvdata(dev);
214	struct pattern_trig_data *data = led_cdev->trigger_data;
215	int err, res;
216
217	err = kstrtos32(buf, 10, &res);
218	if (err)
219		return err;
220
221	/* Number 0 and negative numbers except -1 are invalid. */
222	if (res < -1 || res == 0)
223		return -EINVAL;
224
225	mutex_lock(&data->lock);
226
227	pattern_trig_timer_cancel(data);
228
229	if (data->type == PATTERN_TYPE_HW)
230		led_cdev->pattern_clear(led_cdev);
231
232	data->last_repeat = data->repeat = res;
233	/* -1 means repeat indefinitely */
234	if (data->repeat == -1)
235		data->is_indefinite = true;
236	else
237		data->is_indefinite = false;
238
239	err = pattern_trig_start_pattern(led_cdev);
240
241	mutex_unlock(&data->lock);
242	return err < 0 ? err : count;
243}
244
245static DEVICE_ATTR_RW(repeat);
246
247static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
248					  char *buf, enum pattern_type type)
249{
250	ssize_t count = 0;
251	int i;
252
253	mutex_lock(&data->lock);
254
255	if (!data->npatterns || data->type != type)
256		goto out;
257
258	for (i = 0; i < data->npatterns; i++) {
259		count += scnprintf(buf + count, PAGE_SIZE - count,
260				   "%d %u ",
261				   data->patterns[i].brightness,
262				   data->patterns[i].delta_t);
263	}
264
265	buf[count - 1] = '\n';
266
267out:
268	mutex_unlock(&data->lock);
269	return count;
270}
271
272static int pattern_trig_store_patterns_string(struct pattern_trig_data *data,
273					      const char *buf, size_t count)
274{
275	int ccount, cr, offset = 0;
276
277	while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
278		cr = 0;
279		ccount = sscanf(buf + offset, "%u %u %n",
280				&data->patterns[data->npatterns].brightness,
281				&data->patterns[data->npatterns].delta_t, &cr);
282
283		if (ccount != 2 ||
284		    data->patterns[data->npatterns].brightness > data->led_cdev->max_brightness) {
285			data->npatterns = 0;
286			return -EINVAL;
287		}
288
289		offset += cr;
290		data->npatterns++;
291	}
292
293	return 0;
294}
295
296static int pattern_trig_store_patterns_int(struct pattern_trig_data *data,
297					   const u32 *buf, size_t count)
298{
299	unsigned int i;
300
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];
304		data->npatterns++;
305	}
306
307	return 0;
308}
309
310static 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)
313{
314	struct pattern_trig_data *data = led_cdev->trigger_data;
315	int err = 0;
316
317	mutex_lock(&data->lock);
318
319	pattern_trig_timer_cancel(data);
320
321	if (data->type == PATTERN_TYPE_HW)
322		led_cdev->pattern_clear(led_cdev);
323
324	data->type = type;
325	data->npatterns = 0;
326
327	if (buf)
328		err = pattern_trig_store_patterns_string(data, buf, count);
329	else
330		err = pattern_trig_store_patterns_int(data, buf_int, count);
331	if (err)
332		goto out;
333
334	err = pattern_trig_start_pattern(led_cdev);
335	if (err)
336		data->npatterns = 0;
337
338out:
339	mutex_unlock(&data->lock);
340	return err < 0 ? err : count;
341}
342
343static ssize_t pattern_show(struct device *dev, struct device_attribute *attr,
344			    char *buf)
345{
346	struct led_classdev *led_cdev = dev_get_drvdata(dev);
347	struct pattern_trig_data *data = led_cdev->trigger_data;
348
349	return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_SW);
350}
351
352static ssize_t pattern_store(struct device *dev, struct device_attribute *attr,
353			     const char *buf, size_t count)
354{
355	struct led_classdev *led_cdev = dev_get_drvdata(dev);
356
357	return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
358					   PATTERN_TYPE_SW);
359}
360
361static DEVICE_ATTR_RW(pattern);
362
363static ssize_t hw_pattern_show(struct device *dev,
364			       struct device_attribute *attr, char *buf)
365{
366	struct led_classdev *led_cdev = dev_get_drvdata(dev);
367	struct pattern_trig_data *data = led_cdev->trigger_data;
368
369	return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HW);
370}
371
372static ssize_t hw_pattern_store(struct device *dev,
373				struct device_attribute *attr,
374				const char *buf, size_t count)
375{
376	struct led_classdev *led_cdev = dev_get_drvdata(dev);
377
378	return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
379					   PATTERN_TYPE_HW);
380}
381
382static DEVICE_ATTR_RW(hw_pattern);
383
384static ssize_t hr_pattern_show(struct device *dev,
385			       struct device_attribute *attr, char *buf)
386{
387	struct led_classdev *led_cdev = dev_get_drvdata(dev);
388	struct pattern_trig_data *data = led_cdev->trigger_data;
389
390	return pattern_trig_show_patterns(data, buf, PATTERN_TYPE_HR);
391}
392
393static ssize_t hr_pattern_store(struct device *dev,
394				struct device_attribute *attr,
395				const char *buf, size_t count)
396{
397	struct led_classdev *led_cdev = dev_get_drvdata(dev);
398
399	return pattern_trig_store_patterns(led_cdev, buf, NULL, count,
400					   PATTERN_TYPE_HR);
401}
402
403static DEVICE_ATTR_RW(hr_pattern);
404
405static umode_t pattern_trig_attrs_mode(struct kobject *kobj,
406				       struct attribute *attr, int index)
407{
408	struct device *dev = kobj_to_dev(kobj);
409	struct led_classdev *led_cdev = dev_get_drvdata(dev);
410
411	if (attr == &dev_attr_repeat.attr || attr == &dev_attr_pattern.attr)
412		return attr->mode;
413	else if (attr == &dev_attr_hr_pattern.attr)
414		return attr->mode;
415	else if (attr == &dev_attr_hw_pattern.attr && led_cdev->pattern_set)
416		return attr->mode;
417
418	return 0;
419}
420
421static 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,
426	NULL
427};
428
429static const struct attribute_group pattern_trig_group = {
430	.attrs = pattern_trig_attrs,
431	.is_visible = pattern_trig_attrs_mode,
432};
433
434static const struct attribute_group *pattern_trig_groups[] = {
435	&pattern_trig_group,
436	NULL,
437};
438
439static void pattern_init(struct led_classdev *led_cdev)
440{
441	unsigned int size = 0;
442	u32 *pattern;
443	int err;
444
445	pattern = led_get_default_pattern(led_cdev, &size);
446	if (!pattern)
447		return;
448
449	if (size % 2) {
450		dev_warn(led_cdev->dev, "Expected pattern of tuples\n");
451		goto out;
452	}
453
454	err = pattern_trig_store_patterns(led_cdev, NULL, pattern, size,
455					  PATTERN_TYPE_SW);
456	if (err < 0)
457		dev_warn(led_cdev->dev,
458			 "Pattern initialization failed with error %d\n", err);
459
460out:
461	kfree(pattern);
462}
463
464static int pattern_trig_activate(struct led_classdev *led_cdev)
465{
466	struct pattern_trig_data *data;
467
468	data = kzalloc(sizeof(*data), GFP_KERNEL);
469	if (!data)
470		return -ENOMEM;
471
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;
477	}
478
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;
489
490	if (led_cdev->flags & LED_INIT_DEFAULT_TRIGGER) {
491		pattern_init(led_cdev);
492		/*
493		 * Mark as initialized even on pattern_init() error because
494		 * any consecutive call to it would produce the same error.
495		 */
496		led_cdev->flags &= ~LED_INIT_DEFAULT_TRIGGER;
497	}
498
499	return 0;
500}
501
502static void pattern_trig_deactivate(struct led_classdev *led_cdev)
503{
504	struct pattern_trig_data *data = led_cdev->trigger_data;
505
506	if (!led_cdev->activated)
507		return;
508
509	if (led_cdev->pattern_clear)
510		led_cdev->pattern_clear(led_cdev);
511
512	timer_shutdown_sync(&data->timer);
513	hrtimer_cancel(&data->hrtimer);
514
515	led_set_brightness(led_cdev, LED_OFF);
516	kfree(data);
517	led_cdev->activated = false;
518}
519
520static struct led_trigger pattern_led_trigger = {
521	.name = "pattern",
522	.activate = pattern_trig_activate,
523	.deactivate = pattern_trig_deactivate,
524	.groups = pattern_trig_groups,
525};
526
527static int __init pattern_trig_init(void)
528{
529	return led_trigger_register(&pattern_led_trigger);
530}
531
532static void __exit pattern_trig_exit(void)
533{
534	led_trigger_unregister(&pattern_led_trigger);
535}
536
537module_init(pattern_trig_init);
538module_exit(pattern_trig_exit);
539
540MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com>");
541MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
542MODULE_DESCRIPTION("LED Pattern trigger");
543MODULE_LICENSE("GPL v2");