xref: /linux/drivers/video/backlight/backlight.c (revision 4f3865fb57a04db7cca068fed1c15badc064a302)
1 /*
2  * Backlight Lowlevel Control Abstraction
3  *
4  * Copyright (C) 2003,2004 Hewlett-Packard Company
5  *
6  */
7 
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/device.h>
11 #include <linux/backlight.h>
12 #include <linux/notifier.h>
13 #include <linux/ctype.h>
14 #include <linux/err.h>
15 #include <linux/fb.h>
16 
17 static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
18 {
19 	int rc = -ENXIO;
20 	struct backlight_device *bd = to_backlight_device(cdev);
21 
22 	down(&bd->sem);
23 	if (likely(bd->props))
24 		rc = sprintf(buf, "%d\n", bd->props->power);
25 	up(&bd->sem);
26 
27 	return rc;
28 }
29 
30 static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count)
31 {
32 	int rc = -ENXIO;
33 	char *endp;
34 	struct backlight_device *bd = to_backlight_device(cdev);
35 	int power = simple_strtoul(buf, &endp, 0);
36 	size_t size = endp - buf;
37 
38 	if (*endp && isspace(*endp))
39 		size++;
40 	if (size != count)
41 		return -EINVAL;
42 
43 	down(&bd->sem);
44 	if (likely(bd->props)) {
45 		pr_debug("backlight: set power to %d\n", power);
46 		bd->props->power = power;
47 		if (likely(bd->props->update_status))
48 			bd->props->update_status(bd);
49 		rc = count;
50 	}
51 	up(&bd->sem);
52 
53 	return rc;
54 }
55 
56 static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
57 {
58 	int rc = -ENXIO;
59 	struct backlight_device *bd = to_backlight_device(cdev);
60 
61 	down(&bd->sem);
62 	if (likely(bd->props))
63 		rc = sprintf(buf, "%d\n", bd->props->brightness);
64 	up(&bd->sem);
65 
66 	return rc;
67 }
68 
69 static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count)
70 {
71 	int rc = -ENXIO;
72 	char *endp;
73 	struct backlight_device *bd = to_backlight_device(cdev);
74 	int brightness = simple_strtoul(buf, &endp, 0);
75 	size_t size = endp - buf;
76 
77 	if (*endp && isspace(*endp))
78 		size++;
79 	if (size != count)
80 		return -EINVAL;
81 
82 	down(&bd->sem);
83 	if (likely(bd->props)) {
84 		if (brightness > bd->props->max_brightness)
85 			rc = -EINVAL;
86 		else {
87 			pr_debug("backlight: set brightness to %d\n",
88 				 brightness);
89 			bd->props->brightness = brightness;
90 			if (likely(bd->props->update_status))
91 				bd->props->update_status(bd);
92 			rc = count;
93 		}
94 	}
95 	up(&bd->sem);
96 
97 	return rc;
98 }
99 
100 static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf)
101 {
102 	int rc = -ENXIO;
103 	struct backlight_device *bd = to_backlight_device(cdev);
104 
105 	down(&bd->sem);
106 	if (likely(bd->props))
107 		rc = sprintf(buf, "%d\n", bd->props->max_brightness);
108 	up(&bd->sem);
109 
110 	return rc;
111 }
112 
113 static ssize_t backlight_show_actual_brightness(struct class_device *cdev,
114 						char *buf)
115 {
116 	int rc = -ENXIO;
117 	struct backlight_device *bd = to_backlight_device(cdev);
118 
119 	down(&bd->sem);
120 	if (likely(bd->props && bd->props->get_brightness))
121 		rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd));
122 	up(&bd->sem);
123 
124 	return rc;
125 }
126 
127 static void backlight_class_release(struct class_device *dev)
128 {
129 	struct backlight_device *bd = to_backlight_device(dev);
130 	kfree(bd);
131 }
132 
133 static struct class backlight_class = {
134 	.name = "backlight",
135 	.release = backlight_class_release,
136 };
137 
138 #define DECLARE_ATTR(_name,_mode,_show,_store)			\
139 {							 	\
140 	.attr	= { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE },	\
141 	.show	= _show,					\
142 	.store	= _store,					\
143 }
144 
145 static struct class_device_attribute bl_class_device_attributes[] = {
146 	DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power),
147 	DECLARE_ATTR(brightness, 0644, backlight_show_brightness,
148 		     backlight_store_brightness),
149 	DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
150 		     NULL),
151 	DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
152 };
153 
154 /* This callback gets called when something important happens inside a
155  * framebuffer driver. We're looking if that important event is blanking,
156  * and if it is, we're switching backlight power as well ...
157  */
158 static int fb_notifier_callback(struct notifier_block *self,
159 				unsigned long event, void *data)
160 {
161 	struct backlight_device *bd;
162 	struct fb_event *evdata =(struct fb_event *)data;
163 
164 	/* If we aren't interested in this event, skip it immediately ... */
165 	if (event != FB_EVENT_BLANK)
166 		return 0;
167 
168 	bd = container_of(self, struct backlight_device, fb_notif);
169 	down(&bd->sem);
170 	if (bd->props)
171 		if (!bd->props->check_fb ||
172 		    bd->props->check_fb(evdata->info)) {
173 			bd->props->fb_blank = *(int *)evdata->data;
174 			if (likely(bd->props && bd->props->update_status))
175 				bd->props->update_status(bd);
176 		}
177 	up(&bd->sem);
178 	return 0;
179 }
180 
181 /**
182  * backlight_device_register - create and register a new object of
183  *   backlight_device class.
184  * @name: the name of the new object(must be the same as the name of the
185  *   respective framebuffer device).
186  * @devdata: an optional pointer to be stored in the class_device. The
187  *   methods may retrieve it by using class_get_devdata(&bd->class_dev).
188  * @bp: the backlight properties structure.
189  *
190  * Creates and registers new backlight class_device. Returns either an
191  * ERR_PTR() or a pointer to the newly allocated device.
192  */
193 struct backlight_device *backlight_device_register(const char *name, void *devdata,
194 						   struct backlight_properties *bp)
195 {
196 	int i, rc;
197 	struct backlight_device *new_bd;
198 
199 	pr_debug("backlight_device_alloc: name=%s\n", name);
200 
201 	new_bd = kmalloc(sizeof(struct backlight_device), GFP_KERNEL);
202 	if (unlikely(!new_bd))
203 		return ERR_PTR(-ENOMEM);
204 
205 	init_MUTEX(&new_bd->sem);
206 	new_bd->props = bp;
207 	memset(&new_bd->class_dev, 0, sizeof(new_bd->class_dev));
208 	new_bd->class_dev.class = &backlight_class;
209 	strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN);
210 	class_set_devdata(&new_bd->class_dev, devdata);
211 
212 	rc = class_device_register(&new_bd->class_dev);
213 	if (unlikely(rc)) {
214 error:		kfree(new_bd);
215 		return ERR_PTR(rc);
216 	}
217 
218 	memset(&new_bd->fb_notif, 0, sizeof(new_bd->fb_notif));
219 	new_bd->fb_notif.notifier_call = fb_notifier_callback;
220 
221 	rc = fb_register_client(&new_bd->fb_notif);
222 	if (unlikely(rc))
223 		goto error;
224 
225 	for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) {
226 		rc = class_device_create_file(&new_bd->class_dev,
227 					      &bl_class_device_attributes[i]);
228 		if (unlikely(rc)) {
229 			while (--i >= 0)
230 				class_device_remove_file(&new_bd->class_dev,
231 							 &bl_class_device_attributes[i]);
232 			class_device_unregister(&new_bd->class_dev);
233 			/* No need to kfree(new_bd) since release() method was called */
234 			return ERR_PTR(rc);
235 		}
236 	}
237 
238 	return new_bd;
239 }
240 EXPORT_SYMBOL(backlight_device_register);
241 
242 /**
243  * backlight_device_unregister - unregisters a backlight device object.
244  * @bd: the backlight device object to be unregistered and freed.
245  *
246  * Unregisters a previously registered via backlight_device_register object.
247  */
248 void backlight_device_unregister(struct backlight_device *bd)
249 {
250 	int i;
251 
252 	if (!bd)
253 		return;
254 
255 	pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id);
256 
257 	for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++)
258 		class_device_remove_file(&bd->class_dev,
259 					 &bl_class_device_attributes[i]);
260 
261 	down(&bd->sem);
262 	if (likely(bd->props && bd->props->update_status)) {
263 		bd->props->brightness = 0;
264 		bd->props->power = 0;
265 		bd->props->update_status(bd);
266 	}
267 
268 	bd->props = NULL;
269 	up(&bd->sem);
270 
271 	fb_unregister_client(&bd->fb_notif);
272 
273 	class_device_unregister(&bd->class_dev);
274 }
275 EXPORT_SYMBOL(backlight_device_unregister);
276 
277 static void __exit backlight_class_exit(void)
278 {
279 	class_unregister(&backlight_class);
280 }
281 
282 static int __init backlight_class_init(void)
283 {
284 	return class_register(&backlight_class);
285 }
286 
287 /*
288  * if this is compiled into the kernel, we need to ensure that the
289  * class is registered before users of the class try to register lcd's
290  */
291 postcore_initcall(backlight_class_init);
292 module_exit(backlight_class_exit);
293 
294 MODULE_LICENSE("GPL");
295 MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
296 MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
297