^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");