blob: 9601bfe309ac2744c922a1f612eb4b6188e12aa0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Backlight Lowlevel Control Abstraction
3 *
4 * Copyright (C) 2003,2004 Hewlett-Packard Company
5 *
6 */
7
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
James Simmons3d5eead2006-12-08 02:40:47 -080017
18#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
19 defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
20/* This callback gets called when something important happens inside a
21 * framebuffer driver. We're looking if that important event is blanking,
22 * and if it is, we're switching backlight power as well ...
23 */
24static int fb_notifier_callback(struct notifier_block *self,
25 unsigned long event, void *data)
26{
27 struct backlight_device *bd;
28 struct fb_event *evdata = data;
29
30 /* If we aren't interested in this event, skip it immediately ... */
31 if (event != FB_EVENT_BLANK)
32 return 0;
33
34 bd = container_of(self, struct backlight_device, fb_notif);
35 down(&bd->sem);
36 if (bd->props)
37 if (!bd->props->check_fb ||
38 bd->props->check_fb(evdata->info)) {
39 bd->props->fb_blank = *(int *)evdata->data;
40 if (likely(bd->props && bd->props->update_status))
41 bd->props->update_status(bd);
42 }
43 up(&bd->sem);
44 return 0;
45}
46
47static int backlight_register_fb(struct backlight_device *bd)
48{
49 memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));
50 bd->fb_notif.notifier_call = fb_notifier_callback;
51
52 return fb_register_client(&bd->fb_notif);
53}
54
55static void backlight_unregister_fb(struct backlight_device *bd)
56{
57 fb_unregister_client(&bd->fb_notif);
58}
59#else
60static inline int backlight_register_fb(struct backlight_device *bd)
61{
62 return 0;
63}
64
65static inline void backlight_unregister_fb(struct backlight_device *bd)
66{
67}
68#endif /* CONFIG_FB */
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
71{
Richard Purdie6ca01762006-03-31 02:31:49 -080072 int rc = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 struct backlight_device *bd = to_backlight_device(cdev);
74
75 down(&bd->sem);
Richard Purdie6ca01762006-03-31 02:31:49 -080076 if (likely(bd->props))
77 rc = sprintf(buf, "%d\n", bd->props->power);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 up(&bd->sem);
79
80 return rc;
81}
82
83static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count)
84{
Richard Purdie68673af2006-05-15 09:44:15 -070085 int rc = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 char *endp;
87 struct backlight_device *bd = to_backlight_device(cdev);
Richard Purdie68673af2006-05-15 09:44:15 -070088 int power = simple_strtoul(buf, &endp, 0);
89 size_t size = endp - buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
Richard Purdie68673af2006-05-15 09:44:15 -070091 if (*endp && isspace(*endp))
92 size++;
93 if (size != count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 return -EINVAL;
95
96 down(&bd->sem);
Richard Purdie6ca01762006-03-31 02:31:49 -080097 if (likely(bd->props)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 pr_debug("backlight: set power to %d\n", power);
Richard Purdie6ca01762006-03-31 02:31:49 -080099 bd->props->power = power;
100 if (likely(bd->props->update_status))
101 bd->props->update_status(bd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 rc = count;
Richard Purdie6ca01762006-03-31 02:31:49 -0800103 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 up(&bd->sem);
105
106 return rc;
107}
108
109static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
110{
Richard Purdie6ca01762006-03-31 02:31:49 -0800111 int rc = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 struct backlight_device *bd = to_backlight_device(cdev);
113
114 down(&bd->sem);
Richard Purdie6ca01762006-03-31 02:31:49 -0800115 if (likely(bd->props))
116 rc = sprintf(buf, "%d\n", bd->props->brightness);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 up(&bd->sem);
118
119 return rc;
120}
121
122static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count)
123{
Richard Purdie68673af2006-05-15 09:44:15 -0700124 int rc = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 char *endp;
126 struct backlight_device *bd = to_backlight_device(cdev);
Richard Purdie68673af2006-05-15 09:44:15 -0700127 int brightness = simple_strtoul(buf, &endp, 0);
128 size_t size = endp - buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
Richard Purdie68673af2006-05-15 09:44:15 -0700130 if (*endp && isspace(*endp))
131 size++;
132 if (size != count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 return -EINVAL;
134
135 down(&bd->sem);
Richard Purdie6ca01762006-03-31 02:31:49 -0800136 if (likely(bd->props)) {
137 if (brightness > bd->props->max_brightness)
138 rc = -EINVAL;
139 else {
140 pr_debug("backlight: set brightness to %d\n",
141 brightness);
142 bd->props->brightness = brightness;
143 if (likely(bd->props->update_status))
144 bd->props->update_status(bd);
145 rc = count;
146 }
147 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 up(&bd->sem);
149
150 return rc;
151}
152
153static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf)
154{
Richard Purdie6ca01762006-03-31 02:31:49 -0800155 int rc = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 struct backlight_device *bd = to_backlight_device(cdev);
157
158 down(&bd->sem);
159 if (likely(bd->props))
160 rc = sprintf(buf, "%d\n", bd->props->max_brightness);
Richard Purdie6ca01762006-03-31 02:31:49 -0800161 up(&bd->sem);
162
163 return rc;
164}
165
166static ssize_t backlight_show_actual_brightness(struct class_device *cdev,
167 char *buf)
168{
169 int rc = -ENXIO;
170 struct backlight_device *bd = to_backlight_device(cdev);
171
172 down(&bd->sem);
173 if (likely(bd->props && bd->props->get_brightness))
174 rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 up(&bd->sem);
176
177 return rc;
178}
179
180static void backlight_class_release(struct class_device *dev)
181{
182 struct backlight_device *bd = to_backlight_device(dev);
183 kfree(bd);
184}
185
186static struct class backlight_class = {
187 .name = "backlight",
188 .release = backlight_class_release,
189};
190
191#define DECLARE_ATTR(_name,_mode,_show,_store) \
192{ \
193 .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \
194 .show = _show, \
195 .store = _store, \
196}
197
Helge Dellerd95159c2006-12-08 02:40:28 -0800198static const struct class_device_attribute bl_class_device_attributes[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power),
Richard Purdie6ca01762006-03-31 02:31:49 -0800200 DECLARE_ATTR(brightness, 0644, backlight_show_brightness,
201 backlight_store_brightness),
202 DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
203 NULL),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
205};
206
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207/**
208 * backlight_device_register - create and register a new object of
209 * backlight_device class.
210 * @name: the name of the new object(must be the same as the name of the
211 * respective framebuffer device).
212 * @devdata: an optional pointer to be stored in the class_device. The
213 * methods may retrieve it by using class_get_devdata(&bd->class_dev).
214 * @bp: the backlight properties structure.
215 *
216 * Creates and registers new backlight class_device. Returns either an
217 * ERR_PTR() or a pointer to the newly allocated device.
218 */
Yu Luming519ab5f2006-12-19 12:56:15 -0800219struct backlight_device *backlight_device_register(const char *name,
220 struct device *dev,
221 void *devdata,
222 struct backlight_properties *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223{
224 int i, rc;
225 struct backlight_device *new_bd;
226
227 pr_debug("backlight_device_alloc: name=%s\n", name);
228
229 new_bd = kmalloc(sizeof(struct backlight_device), GFP_KERNEL);
230 if (unlikely(!new_bd))
Jean Delvare10ad1b72006-03-09 17:33:36 -0800231 return ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
233 init_MUTEX(&new_bd->sem);
234 new_bd->props = bp;
235 memset(&new_bd->class_dev, 0, sizeof(new_bd->class_dev));
236 new_bd->class_dev.class = &backlight_class;
Yu Luming519ab5f2006-12-19 12:56:15 -0800237 new_bd->class_dev.dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN);
239 class_set_devdata(&new_bd->class_dev, devdata);
240
241 rc = class_device_register(&new_bd->class_dev);
242 if (unlikely(rc)) {
243error: kfree(new_bd);
244 return ERR_PTR(rc);
245 }
246
James Simmons3d5eead2006-12-08 02:40:47 -0800247 rc = backlight_register_fb(new_bd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 if (unlikely(rc))
249 goto error;
250
251 for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) {
252 rc = class_device_create_file(&new_bd->class_dev,
253 &bl_class_device_attributes[i]);
254 if (unlikely(rc)) {
255 while (--i >= 0)
256 class_device_remove_file(&new_bd->class_dev,
257 &bl_class_device_attributes[i]);
258 class_device_unregister(&new_bd->class_dev);
259 /* No need to kfree(new_bd) since release() method was called */
260 return ERR_PTR(rc);
261 }
262 }
263
264 return new_bd;
265}
266EXPORT_SYMBOL(backlight_device_register);
267
268/**
269 * backlight_device_unregister - unregisters a backlight device object.
270 * @bd: the backlight device object to be unregistered and freed.
271 *
272 * Unregisters a previously registered via backlight_device_register object.
273 */
274void backlight_device_unregister(struct backlight_device *bd)
275{
276 int i;
277
278 if (!bd)
279 return;
280
281 pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id);
282
283 for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++)
284 class_device_remove_file(&bd->class_dev,
285 &bl_class_device_attributes[i]);
286
287 down(&bd->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 bd->props = NULL;
289 up(&bd->sem);
290
James Simmons3d5eead2006-12-08 02:40:47 -0800291 backlight_unregister_fb(bd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293 class_device_unregister(&bd->class_dev);
294}
295EXPORT_SYMBOL(backlight_device_unregister);
296
297static void __exit backlight_class_exit(void)
298{
299 class_unregister(&backlight_class);
300}
301
302static int __init backlight_class_init(void)
303{
304 return class_register(&backlight_class);
305}
306
307/*
308 * if this is compiled into the kernel, we need to ensure that the
309 * class is registered before users of the class try to register lcd's
310 */
311postcore_initcall(backlight_class_init);
312module_exit(backlight_class_exit);
313
314MODULE_LICENSE("GPL");
315MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
316MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");