at master 3.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Backlight emulation LED trigger 4 * 5 * Copyright 2008 (C) Rodolfo Giometti <giometti@linux.it> 6 * Copyright 2008 (C) Eurotech S.p.A. <info@eurotech.it> 7 */ 8 9#include <linux/module.h> 10#include <linux/kernel.h> 11#include <linux/slab.h> 12#include <linux/init.h> 13#include <linux/leds.h> 14#include "../leds.h" 15 16#define BLANK 1 17#define UNBLANK 0 18 19struct bl_trig_notifier { 20 struct led_classdev *led; 21 int brightness; 22 int old_status; 23 unsigned invert; 24 25 struct list_head entry; 26}; 27 28static DEFINE_MUTEX(ledtrig_backlight_list_mutex); 29static LIST_HEAD(ledtrig_backlight_list); 30 31static void ledtrig_backlight_notify_blank(struct bl_trig_notifier *n, int new_status) 32{ 33 struct led_classdev *led = n->led; 34 35 if (new_status == n->old_status) 36 return; 37 38 if ((n->old_status == UNBLANK) ^ n->invert) { 39 n->brightness = led->brightness; 40 led_set_brightness_nosleep(led, LED_OFF); 41 } else { 42 led_set_brightness_nosleep(led, n->brightness); 43 } 44 45 n->old_status = new_status; 46} 47 48void ledtrig_backlight_blank(bool blank) 49{ 50 struct bl_trig_notifier *n; 51 int new_status = blank ? BLANK : UNBLANK; 52 53 guard(mutex)(&ledtrig_backlight_list_mutex); 54 55 list_for_each_entry(n, &ledtrig_backlight_list, entry) 56 ledtrig_backlight_notify_blank(n, new_status); 57} 58EXPORT_SYMBOL(ledtrig_backlight_blank); 59 60static ssize_t bl_trig_invert_show(struct device *dev, 61 struct device_attribute *attr, char *buf) 62{ 63 struct bl_trig_notifier *n = led_trigger_get_drvdata(dev); 64 65 return sprintf(buf, "%u\n", n->invert); 66} 67 68static ssize_t bl_trig_invert_store(struct device *dev, 69 struct device_attribute *attr, const char *buf, size_t num) 70{ 71 struct led_classdev *led = led_trigger_get_led(dev); 72 struct bl_trig_notifier *n = led_trigger_get_drvdata(dev); 73 unsigned long invert; 74 int ret; 75 76 ret = kstrtoul(buf, 10, &invert); 77 if (ret < 0) 78 return ret; 79 80 if (invert > 1) 81 return -EINVAL; 82 83 n->invert = invert; 84 85 /* After inverting, we need to update the LED. */ 86 if ((n->old_status == BLANK) ^ n->invert) 87 led_set_brightness_nosleep(led, LED_OFF); 88 else 89 led_set_brightness_nosleep(led, n->brightness); 90 91 return num; 92} 93static DEVICE_ATTR(inverted, 0644, bl_trig_invert_show, bl_trig_invert_store); 94 95static struct attribute *bl_trig_attrs[] = { 96 &dev_attr_inverted.attr, 97 NULL, 98}; 99ATTRIBUTE_GROUPS(bl_trig); 100 101static int bl_trig_activate(struct led_classdev *led) 102{ 103 struct bl_trig_notifier *n; 104 105 n = kzalloc(sizeof(struct bl_trig_notifier), GFP_KERNEL); 106 if (!n) 107 return -ENOMEM; 108 led_set_trigger_data(led, n); 109 110 n->led = led; 111 n->brightness = led->brightness; 112 n->old_status = UNBLANK; 113 114 guard(mutex)(&ledtrig_backlight_list_mutex); 115 list_add(&n->entry, &ledtrig_backlight_list); 116 117 return 0; 118} 119 120static void bl_trig_deactivate(struct led_classdev *led) 121{ 122 struct bl_trig_notifier *n = led_get_trigger_data(led); 123 124 guard(mutex)(&ledtrig_backlight_list_mutex); 125 list_del(&n->entry); 126 127 kfree(n); 128} 129 130static struct led_trigger bl_led_trigger = { 131 .name = "backlight", 132 .activate = bl_trig_activate, 133 .deactivate = bl_trig_deactivate, 134 .groups = bl_trig_groups, 135}; 136module_led_trigger(bl_led_trigger); 137 138MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); 139MODULE_DESCRIPTION("Backlight emulation LED trigger"); 140MODULE_LICENSE("GPL v2");