blob: e207810bba3cfe9e0042f02bc447ad5b01564ff0 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090016#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017
Richard Purdie321709c2007-02-10 15:04:08 +000018#ifdef CONFIG_PMAC_BACKLIGHT
19#include <asm/backlight.h>
20#endif
James Simmons3d5eead2006-12-08 02:40:47 -080021
22#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
23 defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
24/* This callback gets called when something important happens inside a
25 * framebuffer driver. We're looking if that important event is blanking,
26 * and if it is, we're switching backlight power as well ...
27 */
28static int fb_notifier_callback(struct notifier_block *self,
29 unsigned long event, void *data)
30{
31 struct backlight_device *bd;
32 struct fb_event *evdata = data;
33
34 /* If we aren't interested in this event, skip it immediately ... */
Richard Purdie994efac2007-02-09 09:46:45 +000035 if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
James Simmons3d5eead2006-12-08 02:40:47 -080036 return 0;
37
38 bd = container_of(self, struct backlight_device, fb_notif);
Richard Purdie599a52d2007-02-10 23:07:48 +000039 mutex_lock(&bd->ops_lock);
40 if (bd->ops)
41 if (!bd->ops->check_fb ||
Bruno Prémont57e148b2010-02-21 00:20:01 +010042 bd->ops->check_fb(bd, evdata->info)) {
Richard Purdie599a52d2007-02-10 23:07:48 +000043 bd->props.fb_blank = *(int *)evdata->data;
Richard Purdiec835ee72009-01-06 21:00:19 +000044 if (bd->props.fb_blank == FB_BLANK_UNBLANK)
45 bd->props.state &= ~BL_CORE_FBBLANK;
46 else
47 bd->props.state |= BL_CORE_FBBLANK;
Richard Purdie28ee0862007-02-08 22:25:09 +000048 backlight_update_status(bd);
James Simmons3d5eead2006-12-08 02:40:47 -080049 }
Richard Purdie599a52d2007-02-10 23:07:48 +000050 mutex_unlock(&bd->ops_lock);
James Simmons3d5eead2006-12-08 02:40:47 -080051 return 0;
52}
53
54static int backlight_register_fb(struct backlight_device *bd)
55{
56 memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));
57 bd->fb_notif.notifier_call = fb_notifier_callback;
58
59 return fb_register_client(&bd->fb_notif);
60}
61
62static void backlight_unregister_fb(struct backlight_device *bd)
63{
64 fb_unregister_client(&bd->fb_notif);
65}
66#else
67static inline int backlight_register_fb(struct backlight_device *bd)
68{
69 return 0;
70}
71
72static inline void backlight_unregister_fb(struct backlight_device *bd)
73{
74}
75#endif /* CONFIG_FB */
76
Matthew Garrett325253a2009-07-14 17:06:02 +010077static void backlight_generate_event(struct backlight_device *bd,
78 enum backlight_update_reason reason)
79{
80 char *envp[2];
81
82 switch (reason) {
83 case BACKLIGHT_UPDATE_SYSFS:
84 envp[0] = "SOURCE=sysfs";
85 break;
86 case BACKLIGHT_UPDATE_HOTKEY:
87 envp[0] = "SOURCE=hotkey";
88 break;
89 default:
90 envp[0] = "SOURCE=unknown";
91 break;
92 }
93 envp[1] = NULL;
94 kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);
Henrique de Moraes Holschuh89dfc282009-09-20 14:44:47 -030095 sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
Matthew Garrett325253a2009-07-14 17:06:02 +010096}
97
Richard Purdie655bfd72007-07-09 12:17:24 +010098static ssize_t backlight_show_power(struct device *dev,
99 struct device_attribute *attr,char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100{
Richard Purdie655bfd72007-07-09 12:17:24 +0100101 struct backlight_device *bd = to_backlight_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Richard Purdie599a52d2007-02-10 23:07:48 +0000103 return sprintf(buf, "%d\n", bd->props.power);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104}
105
Richard Purdie655bfd72007-07-09 12:17:24 +0100106static ssize_t backlight_store_power(struct device *dev,
107 struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108{
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000109 int rc;
Richard Purdie655bfd72007-07-09 12:17:24 +0100110 struct backlight_device *bd = to_backlight_device(dev);
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000111 unsigned long power;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000113 rc = strict_strtoul(buf, 0, &power);
114 if (rc)
115 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000117 rc = -ENXIO;
Richard Purdie599a52d2007-02-10 23:07:48 +0000118 mutex_lock(&bd->ops_lock);
119 if (bd->ops) {
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000120 pr_debug("backlight: set power to %lu\n", power);
Helge Deller51552452008-01-13 23:01:13 +0000121 if (bd->props.power != power) {
122 bd->props.power = power;
123 backlight_update_status(bd);
124 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 rc = count;
Richard Purdie6ca01762006-03-31 02:31:49 -0800126 }
Richard Purdie599a52d2007-02-10 23:07:48 +0000127 mutex_unlock(&bd->ops_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
129 return rc;
130}
131
Richard Purdie655bfd72007-07-09 12:17:24 +0100132static ssize_t backlight_show_brightness(struct device *dev,
133 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
Richard Purdie655bfd72007-07-09 12:17:24 +0100135 struct backlight_device *bd = to_backlight_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
Richard Purdie599a52d2007-02-10 23:07:48 +0000137 return sprintf(buf, "%d\n", bd->props.brightness);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138}
139
Richard Purdie655bfd72007-07-09 12:17:24 +0100140static ssize_t backlight_store_brightness(struct device *dev,
141 struct device_attribute *attr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000143 int rc;
Richard Purdie655bfd72007-07-09 12:17:24 +0100144 struct backlight_device *bd = to_backlight_device(dev);
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000145 unsigned long brightness;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000147 rc = strict_strtoul(buf, 0, &brightness);
148 if (rc)
149 return rc;
150
151 rc = -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Richard Purdie599a52d2007-02-10 23:07:48 +0000153 mutex_lock(&bd->ops_lock);
154 if (bd->ops) {
155 if (brightness > bd->props.max_brightness)
Richard Purdie6ca01762006-03-31 02:31:49 -0800156 rc = -EINVAL;
157 else {
Pavel Machek9a2c61a2008-12-03 08:43:48 +0000158 pr_debug("backlight: set brightness to %lu\n",
Richard Purdie6ca01762006-03-31 02:31:49 -0800159 brightness);
Zhang Rui9be1df92009-01-08 14:11:30 +0000160 bd->props.brightness = brightness;
161 backlight_update_status(bd);
Richard Purdie6ca01762006-03-31 02:31:49 -0800162 rc = count;
163 }
164 }
Richard Purdie599a52d2007-02-10 23:07:48 +0000165 mutex_unlock(&bd->ops_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166
Matthew Garrett325253a2009-07-14 17:06:02 +0100167 backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
168
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 return rc;
170}
171
Richard Purdie655bfd72007-07-09 12:17:24 +0100172static ssize_t backlight_show_max_brightness(struct device *dev,
173 struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174{
Richard Purdie655bfd72007-07-09 12:17:24 +0100175 struct backlight_device *bd = to_backlight_device(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
Richard Purdie599a52d2007-02-10 23:07:48 +0000177 return sprintf(buf, "%d\n", bd->props.max_brightness);
Richard Purdie6ca01762006-03-31 02:31:49 -0800178}
179
Richard Purdie655bfd72007-07-09 12:17:24 +0100180static ssize_t backlight_show_actual_brightness(struct device *dev,
181 struct device_attribute *attr, char *buf)
Richard Purdie6ca01762006-03-31 02:31:49 -0800182{
183 int rc = -ENXIO;
Richard Purdie655bfd72007-07-09 12:17:24 +0100184 struct backlight_device *bd = to_backlight_device(dev);
Richard Purdie6ca01762006-03-31 02:31:49 -0800185
Richard Purdie599a52d2007-02-10 23:07:48 +0000186 mutex_lock(&bd->ops_lock);
187 if (bd->ops && bd->ops->get_brightness)
188 rc = sprintf(buf, "%d\n", bd->ops->get_brightness(bd));
189 mutex_unlock(&bd->ops_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
191 return rc;
192}
193
Adrian Bunk0ad90ef2007-08-11 10:27:19 +0100194static struct class *backlight_class;
Richard Purdie655bfd72007-07-09 12:17:24 +0100195
Richard Purdiec835ee72009-01-06 21:00:19 +0000196static int backlight_suspend(struct device *dev, pm_message_t state)
197{
198 struct backlight_device *bd = to_backlight_device(dev);
199
200 if (bd->ops->options & BL_CORE_SUSPENDRESUME) {
201 mutex_lock(&bd->ops_lock);
202 bd->props.state |= BL_CORE_SUSPENDED;
203 backlight_update_status(bd);
204 mutex_unlock(&bd->ops_lock);
205 }
206
207 return 0;
208}
209
210static int backlight_resume(struct device *dev)
211{
212 struct backlight_device *bd = to_backlight_device(dev);
213
214 if (bd->ops->options & BL_CORE_SUSPENDRESUME) {
215 mutex_lock(&bd->ops_lock);
216 bd->props.state &= ~BL_CORE_SUSPENDED;
217 backlight_update_status(bd);
218 mutex_unlock(&bd->ops_lock);
219 }
220
221 return 0;
222}
223
Richard Purdie655bfd72007-07-09 12:17:24 +0100224static void bl_device_release(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
226 struct backlight_device *bd = to_backlight_device(dev);
227 kfree(bd);
228}
229
Richard Purdie655bfd72007-07-09 12:17:24 +0100230static struct device_attribute bl_device_attributes[] = {
231 __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
232 __ATTR(brightness, 0644, backlight_show_brightness,
Richard Purdie6ca01762006-03-31 02:31:49 -0800233 backlight_store_brightness),
Richard Purdie655bfd72007-07-09 12:17:24 +0100234 __ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
Richard Purdie6ca01762006-03-31 02:31:49 -0800235 NULL),
Richard Purdie655bfd72007-07-09 12:17:24 +0100236 __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
237 __ATTR_NULL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238};
239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240/**
Matthew Garrett325253a2009-07-14 17:06:02 +0100241 * backlight_force_update - tell the backlight subsystem that hardware state
242 * has changed
243 * @bd: the backlight device to update
244 *
245 * Updates the internal state of the backlight in response to a hardware event,
246 * and generate a uevent to notify userspace
247 */
248void backlight_force_update(struct backlight_device *bd,
249 enum backlight_update_reason reason)
250{
251 mutex_lock(&bd->ops_lock);
252 if (bd->ops && bd->ops->get_brightness)
253 bd->props.brightness = bd->ops->get_brightness(bd);
254 mutex_unlock(&bd->ops_lock);
255 backlight_generate_event(bd, reason);
256}
257EXPORT_SYMBOL(backlight_force_update);
258
259/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 * backlight_device_register - create and register a new object of
261 * backlight_device class.
262 * @name: the name of the new object(must be the same as the name of the
263 * respective framebuffer device).
Sebastian Siewiorf6ec2d92008-07-16 23:05:49 +0100264 * @parent: a pointer to the parent device
Richard Purdie655bfd72007-07-09 12:17:24 +0100265 * @devdata: an optional pointer to be stored for private driver use. The
266 * methods may retrieve it by using bl_get_data(bd).
Richard Purdie599a52d2007-02-10 23:07:48 +0000267 * @ops: the backlight operations structure.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 *
Richard Purdie655bfd72007-07-09 12:17:24 +0100269 * Creates and registers new backlight device. Returns either an
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 * ERR_PTR() or a pointer to the newly allocated device.
271 */
Yu Luming519ab5f2006-12-19 12:56:15 -0800272struct backlight_device *backlight_device_register(const char *name,
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500273 struct device *parent, void *devdata, const struct backlight_ops *ops,
274 const struct backlight_properties *props)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 struct backlight_device *new_bd;
Richard Purdie655bfd72007-07-09 12:17:24 +0100277 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
Richard Purdie655bfd72007-07-09 12:17:24 +0100279 pr_debug("backlight_device_register: name=%s\n", name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
Richard Purdie599a52d2007-02-10 23:07:48 +0000281 new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
Dmitry Torokhov90968e82007-02-08 00:12:28 +0000282 if (!new_bd)
Jean Delvare10ad1b72006-03-09 17:33:36 -0800283 return ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
Richard Purdie28ee0862007-02-08 22:25:09 +0000285 mutex_init(&new_bd->update_lock);
Richard Purdie599a52d2007-02-10 23:07:48 +0000286 mutex_init(&new_bd->ops_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
Richard Purdie655bfd72007-07-09 12:17:24 +0100288 new_bd->dev.class = backlight_class;
289 new_bd->dev.parent = parent;
290 new_bd->dev.release = bl_device_release;
Kay Sievers64dba9a2009-01-06 10:44:35 -0800291 dev_set_name(&new_bd->dev, name);
Richard Purdie655bfd72007-07-09 12:17:24 +0100292 dev_set_drvdata(&new_bd->dev, devdata);
293
Matthew Garretta19a6ee2010-02-17 16:39:44 -0500294 /* Set default properties */
295 if (props)
296 memcpy(&new_bd->props, props,
297 sizeof(struct backlight_properties));
298
Richard Purdie655bfd72007-07-09 12:17:24 +0100299 rc = device_register(&new_bd->dev);
Dmitry Torokhov90968e82007-02-08 00:12:28 +0000300 if (rc) {
Dmitry Torokhov2fd5a152007-02-07 22:25:50 +0000301 kfree(new_bd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 return ERR_PTR(rc);
303 }
304
James Simmons3d5eead2006-12-08 02:40:47 -0800305 rc = backlight_register_fb(new_bd);
Dmitry Torokhov2fd5a152007-02-07 22:25:50 +0000306 if (rc) {
Richard Purdie655bfd72007-07-09 12:17:24 +0100307 device_unregister(&new_bd->dev);
Dmitry Torokhov2fd5a152007-02-07 22:25:50 +0000308 return ERR_PTR(rc);
309 }
310
Richard Purdie655bfd72007-07-09 12:17:24 +0100311 new_bd->ops = ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
Richard Purdie321709c2007-02-10 15:04:08 +0000313#ifdef CONFIG_PMAC_BACKLIGHT
314 mutex_lock(&pmac_backlight_mutex);
315 if (!pmac_backlight)
316 pmac_backlight = new_bd;
317 mutex_unlock(&pmac_backlight_mutex);
318#endif
319
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 return new_bd;
321}
322EXPORT_SYMBOL(backlight_device_register);
323
324/**
325 * backlight_device_unregister - unregisters a backlight device object.
326 * @bd: the backlight device object to be unregistered and freed.
327 *
328 * Unregisters a previously registered via backlight_device_register object.
329 */
330void backlight_device_unregister(struct backlight_device *bd)
331{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 if (!bd)
333 return;
334
Richard Purdie321709c2007-02-10 15:04:08 +0000335#ifdef CONFIG_PMAC_BACKLIGHT
336 mutex_lock(&pmac_backlight_mutex);
337 if (pmac_backlight == bd)
338 pmac_backlight = NULL;
339 mutex_unlock(&pmac_backlight_mutex);
340#endif
Richard Purdie599a52d2007-02-10 23:07:48 +0000341 mutex_lock(&bd->ops_lock);
342 bd->ops = NULL;
343 mutex_unlock(&bd->ops_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
James Simmons3d5eead2006-12-08 02:40:47 -0800345 backlight_unregister_fb(bd);
Richard Purdie655bfd72007-07-09 12:17:24 +0100346 device_unregister(&bd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347}
348EXPORT_SYMBOL(backlight_device_unregister);
349
350static void __exit backlight_class_exit(void)
351{
Richard Purdie655bfd72007-07-09 12:17:24 +0100352 class_destroy(backlight_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353}
354
355static int __init backlight_class_init(void)
356{
Richard Purdie655bfd72007-07-09 12:17:24 +0100357 backlight_class = class_create(THIS_MODULE, "backlight");
358 if (IS_ERR(backlight_class)) {
359 printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
360 PTR_ERR(backlight_class));
361 return PTR_ERR(backlight_class);
362 }
363
364 backlight_class->dev_attrs = bl_device_attributes;
Richard Purdiec835ee72009-01-06 21:00:19 +0000365 backlight_class->suspend = backlight_suspend;
366 backlight_class->resume = backlight_resume;
Richard Purdie655bfd72007-07-09 12:17:24 +0100367 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368}
369
370/*
371 * if this is compiled into the kernel, we need to ensure that the
372 * class is registered before users of the class try to register lcd's
373 */
374postcore_initcall(backlight_class_init);
375module_exit(backlight_class_exit);
376
377MODULE_LICENSE("GPL");
378MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
379MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");