Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

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