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