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