Commit | Line | Data |
---|---|---|
09c434b8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * Backlight Lowlevel Control Abstraction | |
4 | * | |
5 | * Copyright (C) 2003,2004 Hewlett-Packard Company | |
6 | * | |
7 | */ | |
8 | ||
35f96162 JH |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | ||
1da177e4 LT |
11 | #include <linux/module.h> |
12 | #include <linux/init.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/backlight.h> | |
15 | #include <linux/notifier.h> | |
16 | #include <linux/ctype.h> | |
17 | #include <linux/err.h> | |
18 | #include <linux/fb.h> | |
5a0e3ad6 | 19 | #include <linux/slab.h> |
1da177e4 | 20 | |
321709c5 RP |
21 | #ifdef CONFIG_PMAC_BACKLIGHT |
22 | #include <asm/backlight.h> | |
23 | #endif | |
3d5eeadd | 24 | |
5915a3db AL |
25 | static struct list_head backlight_dev_list; |
26 | static struct mutex backlight_dev_list_mutex; | |
3cc6919b | 27 | static struct blocking_notifier_head backlight_notifier; |
5915a3db | 28 | |
c338bfb5 | 29 | static const char *const backlight_types[] = { |
bb7ca747 MG |
30 | [BACKLIGHT_RAW] = "raw", |
31 | [BACKLIGHT_PLATFORM] = "platform", | |
32 | [BACKLIGHT_FIRMWARE] = "firmware", | |
33 | }; | |
34 | ||
3d5eeadd JS |
35 | #if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \ |
36 | defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)) | |
37 | /* This callback gets called when something important happens inside a | |
38 | * framebuffer driver. We're looking if that important event is blanking, | |
8c16f330 | 39 | * and if it is and necessary, we're switching backlight power as well ... |
3d5eeadd JS |
40 | */ |
41 | static int fb_notifier_callback(struct notifier_block *self, | |
42 | unsigned long event, void *data) | |
43 | { | |
44 | struct backlight_device *bd; | |
45 | struct fb_event *evdata = data; | |
a55944ca LY |
46 | int node = evdata->info->node; |
47 | int fb_blank = 0; | |
3d5eeadd JS |
48 | |
49 | /* If we aren't interested in this event, skip it immediately ... */ | |
994efacd | 50 | if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK) |
3d5eeadd JS |
51 | return 0; |
52 | ||
53 | bd = container_of(self, struct backlight_device, fb_notif); | |
599a52d1 RP |
54 | mutex_lock(&bd->ops_lock); |
55 | if (bd->ops) | |
56 | if (!bd->ops->check_fb || | |
57e148b6 | 57 | bd->ops->check_fb(bd, evdata->info)) { |
a55944ca LY |
58 | fb_blank = *(int *)evdata->data; |
59 | if (fb_blank == FB_BLANK_UNBLANK && | |
60 | !bd->fb_bl_on[node]) { | |
61 | bd->fb_bl_on[node] = true; | |
62 | if (!bd->use_count++) { | |
63 | bd->props.state &= ~BL_CORE_FBBLANK; | |
64 | bd->props.fb_blank = FB_BLANK_UNBLANK; | |
8c16f330 | 65 | backlight_update_status(bd); |
a55944ca LY |
66 | } |
67 | } else if (fb_blank != FB_BLANK_UNBLANK && | |
68 | bd->fb_bl_on[node]) { | |
69 | bd->fb_bl_on[node] = false; | |
70 | if (!(--bd->use_count)) { | |
71 | bd->props.state |= BL_CORE_FBBLANK; | |
72 | bd->props.fb_blank = fb_blank; | |
8c16f330 | 73 | backlight_update_status(bd); |
a55944ca LY |
74 | } |
75 | } | |
3d5eeadd | 76 | } |
599a52d1 | 77 | mutex_unlock(&bd->ops_lock); |
3d5eeadd JS |
78 | return 0; |
79 | } | |
80 | ||
81 | static int backlight_register_fb(struct backlight_device *bd) | |
82 | { | |
83 | memset(&bd->fb_notif, 0, sizeof(bd->fb_notif)); | |
84 | bd->fb_notif.notifier_call = fb_notifier_callback; | |
85 | ||
86 | return fb_register_client(&bd->fb_notif); | |
87 | } | |
88 | ||
89 | static void backlight_unregister_fb(struct backlight_device *bd) | |
90 | { | |
91 | fb_unregister_client(&bd->fb_notif); | |
92 | } | |
93 | #else | |
94 | static inline int backlight_register_fb(struct backlight_device *bd) | |
95 | { | |
96 | return 0; | |
97 | } | |
98 | ||
99 | static inline void backlight_unregister_fb(struct backlight_device *bd) | |
100 | { | |
101 | } | |
102 | #endif /* CONFIG_FB */ | |
103 | ||
325253a6 MG |
104 | static void backlight_generate_event(struct backlight_device *bd, |
105 | enum backlight_update_reason reason) | |
106 | { | |
107 | char *envp[2]; | |
108 | ||
109 | switch (reason) { | |
110 | case BACKLIGHT_UPDATE_SYSFS: | |
111 | envp[0] = "SOURCE=sysfs"; | |
112 | break; | |
113 | case BACKLIGHT_UPDATE_HOTKEY: | |
114 | envp[0] = "SOURCE=hotkey"; | |
115 | break; | |
116 | default: | |
117 | envp[0] = "SOURCE=unknown"; | |
118 | break; | |
119 | } | |
120 | envp[1] = NULL; | |
121 | kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp); | |
89dfc28c | 122 | sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness"); |
325253a6 MG |
123 | } |
124 | ||
ea1bb706 GKH |
125 | static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr, |
126 | char *buf) | |
1da177e4 | 127 | { |
655bfd7a | 128 | struct backlight_device *bd = to_backlight_device(dev); |
1da177e4 | 129 | |
599a52d1 | 130 | return sprintf(buf, "%d\n", bd->props.power); |
1da177e4 LT |
131 | } |
132 | ||
ea1bb706 GKH |
133 | static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr, |
134 | const char *buf, size_t count) | |
1da177e4 | 135 | { |
9a2c61a9 | 136 | int rc; |
655bfd7a | 137 | struct backlight_device *bd = to_backlight_device(dev); |
7e715c2d | 138 | unsigned long power, old_power; |
1da177e4 | 139 | |
66655760 | 140 | rc = kstrtoul(buf, 0, &power); |
9a2c61a9 PM |
141 | if (rc) |
142 | return rc; | |
1da177e4 | 143 | |
9a2c61a9 | 144 | rc = -ENXIO; |
599a52d1 RP |
145 | mutex_lock(&bd->ops_lock); |
146 | if (bd->ops) { | |
35f96162 | 147 | pr_debug("set power to %lu\n", power); |
51552453 | 148 | if (bd->props.power != power) { |
7e715c2d | 149 | old_power = bd->props.power; |
51552453 | 150 | bd->props.power = power; |
7e715c2d SM |
151 | rc = backlight_update_status(bd); |
152 | if (rc) | |
153 | bd->props.power = old_power; | |
154 | else | |
155 | rc = count; | |
156 | } else { | |
157 | rc = count; | |
51552453 | 158 | } |
6ca01765 | 159 | } |
599a52d1 | 160 | mutex_unlock(&bd->ops_lock); |
1da177e4 LT |
161 | |
162 | return rc; | |
163 | } | |
ea1bb706 | 164 | static DEVICE_ATTR_RW(bl_power); |
1da177e4 | 165 | |
ea1bb706 | 166 | static ssize_t brightness_show(struct device *dev, |
655bfd7a | 167 | struct device_attribute *attr, char *buf) |
1da177e4 | 168 | { |
655bfd7a | 169 | struct backlight_device *bd = to_backlight_device(dev); |
1da177e4 | 170 | |
599a52d1 | 171 | return sprintf(buf, "%d\n", bd->props.brightness); |
1da177e4 LT |
172 | } |
173 | ||
f6a4790a AL |
174 | int backlight_device_set_brightness(struct backlight_device *bd, |
175 | unsigned long brightness) | |
1da177e4 | 176 | { |
f6a4790a | 177 | int rc = -ENXIO; |
1da177e4 | 178 | |
599a52d1 RP |
179 | mutex_lock(&bd->ops_lock); |
180 | if (bd->ops) { | |
181 | if (brightness > bd->props.max_brightness) | |
6ca01765 RP |
182 | rc = -EINVAL; |
183 | else { | |
35f96162 | 184 | pr_debug("set brightness to %lu\n", brightness); |
9be1df98 | 185 | bd->props.brightness = brightness; |
7e715c2d | 186 | rc = backlight_update_status(bd); |
6ca01765 RP |
187 | } |
188 | } | |
599a52d1 | 189 | mutex_unlock(&bd->ops_lock); |
1da177e4 | 190 | |
325253a6 MG |
191 | backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); |
192 | ||
1da177e4 LT |
193 | return rc; |
194 | } | |
f6a4790a AL |
195 | EXPORT_SYMBOL(backlight_device_set_brightness); |
196 | ||
197 | static ssize_t brightness_store(struct device *dev, | |
198 | struct device_attribute *attr, const char *buf, size_t count) | |
199 | { | |
200 | int rc; | |
201 | struct backlight_device *bd = to_backlight_device(dev); | |
202 | unsigned long brightness; | |
203 | ||
204 | rc = kstrtoul(buf, 0, &brightness); | |
205 | if (rc) | |
206 | return rc; | |
207 | ||
208 | rc = backlight_device_set_brightness(bd, brightness); | |
209 | ||
210 | return rc ? rc : count; | |
211 | } | |
ea1bb706 | 212 | static DEVICE_ATTR_RW(brightness); |
1da177e4 | 213 | |
ea1bb706 GKH |
214 | static ssize_t type_show(struct device *dev, struct device_attribute *attr, |
215 | char *buf) | |
bb7ca747 MG |
216 | { |
217 | struct backlight_device *bd = to_backlight_device(dev); | |
218 | ||
219 | return sprintf(buf, "%s\n", backlight_types[bd->props.type]); | |
220 | } | |
ea1bb706 | 221 | static DEVICE_ATTR_RO(type); |
bb7ca747 | 222 | |
ea1bb706 | 223 | static ssize_t max_brightness_show(struct device *dev, |
655bfd7a | 224 | struct device_attribute *attr, char *buf) |
1da177e4 | 225 | { |
655bfd7a | 226 | struct backlight_device *bd = to_backlight_device(dev); |
1da177e4 | 227 | |
599a52d1 | 228 | return sprintf(buf, "%d\n", bd->props.max_brightness); |
6ca01765 | 229 | } |
ea1bb706 | 230 | static DEVICE_ATTR_RO(max_brightness); |
6ca01765 | 231 | |
ea1bb706 | 232 | static ssize_t actual_brightness_show(struct device *dev, |
655bfd7a | 233 | struct device_attribute *attr, char *buf) |
6ca01765 RP |
234 | { |
235 | int rc = -ENXIO; | |
655bfd7a | 236 | struct backlight_device *bd = to_backlight_device(dev); |
6ca01765 | 237 | |
599a52d1 RP |
238 | mutex_lock(&bd->ops_lock); |
239 | if (bd->ops && bd->ops->get_brightness) | |
240 | rc = sprintf(buf, "%d\n", bd->ops->get_brightness(bd)); | |
b3de3402 AH |
241 | else |
242 | rc = sprintf(buf, "%d\n", bd->props.brightness); | |
599a52d1 | 243 | mutex_unlock(&bd->ops_lock); |
1da177e4 LT |
244 | |
245 | return rc; | |
246 | } | |
ea1bb706 | 247 | static DEVICE_ATTR_RO(actual_brightness); |
1da177e4 | 248 | |
0ad90efd | 249 | static struct class *backlight_class; |
655bfd7a | 250 | |
3601792e SK |
251 | #ifdef CONFIG_PM_SLEEP |
252 | static int backlight_suspend(struct device *dev) | |
c835ee7f RP |
253 | { |
254 | struct backlight_device *bd = to_backlight_device(dev); | |
255 | ||
d1d73578 UKK |
256 | mutex_lock(&bd->ops_lock); |
257 | if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { | |
c835ee7f RP |
258 | bd->props.state |= BL_CORE_SUSPENDED; |
259 | backlight_update_status(bd); | |
c835ee7f | 260 | } |
d1d73578 | 261 | mutex_unlock(&bd->ops_lock); |
c835ee7f RP |
262 | |
263 | return 0; | |
264 | } | |
265 | ||
266 | static int backlight_resume(struct device *dev) | |
267 | { | |
268 | struct backlight_device *bd = to_backlight_device(dev); | |
269 | ||
d1d73578 UKK |
270 | mutex_lock(&bd->ops_lock); |
271 | if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { | |
c835ee7f RP |
272 | bd->props.state &= ~BL_CORE_SUSPENDED; |
273 | backlight_update_status(bd); | |
c835ee7f | 274 | } |
d1d73578 | 275 | mutex_unlock(&bd->ops_lock); |
c835ee7f RP |
276 | |
277 | return 0; | |
278 | } | |
3601792e SK |
279 | #endif |
280 | ||
281 | static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend, | |
282 | backlight_resume); | |
c835ee7f | 283 | |
655bfd7a | 284 | static void bl_device_release(struct device *dev) |
1da177e4 LT |
285 | { |
286 | struct backlight_device *bd = to_backlight_device(dev); | |
287 | kfree(bd); | |
288 | } | |
289 | ||
ea1bb706 GKH |
290 | static struct attribute *bl_device_attrs[] = { |
291 | &dev_attr_bl_power.attr, | |
292 | &dev_attr_brightness.attr, | |
293 | &dev_attr_actual_brightness.attr, | |
294 | &dev_attr_max_brightness.attr, | |
295 | &dev_attr_type.attr, | |
296 | NULL, | |
1da177e4 | 297 | }; |
ea1bb706 | 298 | ATTRIBUTE_GROUPS(bl_device); |
1da177e4 | 299 | |
325253a6 MG |
300 | /** |
301 | * backlight_force_update - tell the backlight subsystem that hardware state | |
302 | * has changed | |
303 | * @bd: the backlight device to update | |
304 | * | |
305 | * Updates the internal state of the backlight in response to a hardware event, | |
306 | * and generate a uevent to notify userspace | |
307 | */ | |
308 | void backlight_force_update(struct backlight_device *bd, | |
309 | enum backlight_update_reason reason) | |
310 | { | |
311 | mutex_lock(&bd->ops_lock); | |
312 | if (bd->ops && bd->ops->get_brightness) | |
313 | bd->props.brightness = bd->ops->get_brightness(bd); | |
314 | mutex_unlock(&bd->ops_lock); | |
315 | backlight_generate_event(bd, reason); | |
316 | } | |
317 | EXPORT_SYMBOL(backlight_force_update); | |
318 | ||
1da177e4 LT |
319 | /** |
320 | * backlight_device_register - create and register a new object of | |
321 | * backlight_device class. | |
322 | * @name: the name of the new object(must be the same as the name of the | |
323 | * respective framebuffer device). | |
f6ec2d96 | 324 | * @parent: a pointer to the parent device |
655bfd7a RP |
325 | * @devdata: an optional pointer to be stored for private driver use. The |
326 | * methods may retrieve it by using bl_get_data(bd). | |
599a52d1 | 327 | * @ops: the backlight operations structure. |
1da177e4 | 328 | * |
655bfd7a | 329 | * Creates and registers new backlight device. Returns either an |
1da177e4 LT |
330 | * ERR_PTR() or a pointer to the newly allocated device. |
331 | */ | |
519ab5f2 | 332 | struct backlight_device *backlight_device_register(const char *name, |
a19a6ee6 MG |
333 | struct device *parent, void *devdata, const struct backlight_ops *ops, |
334 | const struct backlight_properties *props) | |
1da177e4 | 335 | { |
1da177e4 | 336 | struct backlight_device *new_bd; |
655bfd7a | 337 | int rc; |
1da177e4 | 338 | |
655bfd7a | 339 | pr_debug("backlight_device_register: name=%s\n", name); |
1da177e4 | 340 | |
599a52d1 | 341 | new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); |
90968e8e | 342 | if (!new_bd) |
10ad1b73 | 343 | return ERR_PTR(-ENOMEM); |
1da177e4 | 344 | |
28ee086d | 345 | mutex_init(&new_bd->update_lock); |
599a52d1 | 346 | mutex_init(&new_bd->ops_lock); |
1da177e4 | 347 | |
655bfd7a RP |
348 | new_bd->dev.class = backlight_class; |
349 | new_bd->dev.parent = parent; | |
350 | new_bd->dev.release = bl_device_release; | |
02aa2a37 | 351 | dev_set_name(&new_bd->dev, "%s", name); |
655bfd7a RP |
352 | dev_set_drvdata(&new_bd->dev, devdata); |
353 | ||
a19a6ee6 | 354 | /* Set default properties */ |
bb7ca747 | 355 | if (props) { |
a19a6ee6 MG |
356 | memcpy(&new_bd->props, props, |
357 | sizeof(struct backlight_properties)); | |
bb7ca747 MG |
358 | if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) { |
359 | WARN(1, "%s: invalid backlight type", name); | |
360 | new_bd->props.type = BACKLIGHT_RAW; | |
361 | } | |
362 | } else { | |
363 | new_bd->props.type = BACKLIGHT_RAW; | |
364 | } | |
a19a6ee6 | 365 | |
655bfd7a | 366 | rc = device_register(&new_bd->dev); |
90968e8e | 367 | if (rc) { |
35762a47 | 368 | put_device(&new_bd->dev); |
1da177e4 LT |
369 | return ERR_PTR(rc); |
370 | } | |
371 | ||
3d5eeadd | 372 | rc = backlight_register_fb(new_bd); |
2fd5a154 | 373 | if (rc) { |
655bfd7a | 374 | device_unregister(&new_bd->dev); |
2fd5a154 DT |
375 | return ERR_PTR(rc); |
376 | } | |
377 | ||
655bfd7a | 378 | new_bd->ops = ops; |
1da177e4 | 379 | |
321709c5 RP |
380 | #ifdef CONFIG_PMAC_BACKLIGHT |
381 | mutex_lock(&pmac_backlight_mutex); | |
382 | if (!pmac_backlight) | |
383 | pmac_backlight = new_bd; | |
384 | mutex_unlock(&pmac_backlight_mutex); | |
385 | #endif | |
386 | ||
5915a3db AL |
387 | mutex_lock(&backlight_dev_list_mutex); |
388 | list_add(&new_bd->entry, &backlight_dev_list); | |
389 | mutex_unlock(&backlight_dev_list_mutex); | |
390 | ||
3cc6919b HG |
391 | blocking_notifier_call_chain(&backlight_notifier, |
392 | BACKLIGHT_REGISTERED, new_bd); | |
393 | ||
1da177e4 LT |
394 | return new_bd; |
395 | } | |
396 | EXPORT_SYMBOL(backlight_device_register); | |
397 | ||
f6a4790a | 398 | struct backlight_device *backlight_device_get_by_type(enum backlight_type type) |
5915a3db AL |
399 | { |
400 | bool found = false; | |
401 | struct backlight_device *bd; | |
402 | ||
403 | mutex_lock(&backlight_dev_list_mutex); | |
404 | list_for_each_entry(bd, &backlight_dev_list, entry) { | |
405 | if (bd->props.type == type) { | |
406 | found = true; | |
407 | break; | |
408 | } | |
409 | } | |
410 | mutex_unlock(&backlight_dev_list_mutex); | |
411 | ||
f6a4790a AL |
412 | return found ? bd : NULL; |
413 | } | |
414 | EXPORT_SYMBOL(backlight_device_get_by_type); | |
415 | ||
1da177e4 LT |
416 | /** |
417 | * backlight_device_unregister - unregisters a backlight device object. | |
418 | * @bd: the backlight device object to be unregistered and freed. | |
419 | * | |
420 | * Unregisters a previously registered via backlight_device_register object. | |
421 | */ | |
422 | void backlight_device_unregister(struct backlight_device *bd) | |
423 | { | |
1da177e4 LT |
424 | if (!bd) |
425 | return; | |
426 | ||
5915a3db AL |
427 | mutex_lock(&backlight_dev_list_mutex); |
428 | list_del(&bd->entry); | |
429 | mutex_unlock(&backlight_dev_list_mutex); | |
430 | ||
321709c5 RP |
431 | #ifdef CONFIG_PMAC_BACKLIGHT |
432 | mutex_lock(&pmac_backlight_mutex); | |
433 | if (pmac_backlight == bd) | |
434 | pmac_backlight = NULL; | |
435 | mutex_unlock(&pmac_backlight_mutex); | |
436 | #endif | |
3cc6919b HG |
437 | |
438 | blocking_notifier_call_chain(&backlight_notifier, | |
439 | BACKLIGHT_UNREGISTERED, bd); | |
440 | ||
599a52d1 RP |
441 | mutex_lock(&bd->ops_lock); |
442 | bd->ops = NULL; | |
443 | mutex_unlock(&bd->ops_lock); | |
1da177e4 | 444 | |
3d5eeadd | 445 | backlight_unregister_fb(bd); |
655bfd7a | 446 | device_unregister(&bd->dev); |
1da177e4 LT |
447 | } |
448 | EXPORT_SYMBOL(backlight_device_unregister); | |
449 | ||
8318fde4 JH |
450 | static void devm_backlight_device_release(struct device *dev, void *res) |
451 | { | |
452 | struct backlight_device *backlight = *(struct backlight_device **)res; | |
453 | ||
454 | backlight_device_unregister(backlight); | |
455 | } | |
456 | ||
457 | static int devm_backlight_device_match(struct device *dev, void *res, | |
458 | void *data) | |
459 | { | |
460 | struct backlight_device **r = res; | |
461 | ||
462 | return *r == data; | |
463 | } | |
464 | ||
3cc6919b HG |
465 | /** |
466 | * backlight_register_notifier - get notified of backlight (un)registration | |
467 | * @nb: notifier block with the notifier to call on backlight (un)registration | |
468 | * | |
469 | * @return 0 on success, otherwise a negative error code | |
470 | * | |
471 | * Register a notifier to get notified when backlight devices get registered | |
472 | * or unregistered. | |
473 | */ | |
474 | int backlight_register_notifier(struct notifier_block *nb) | |
475 | { | |
476 | return blocking_notifier_chain_register(&backlight_notifier, nb); | |
477 | } | |
478 | EXPORT_SYMBOL(backlight_register_notifier); | |
479 | ||
480 | /** | |
481 | * backlight_unregister_notifier - unregister a backlight notifier | |
482 | * @nb: notifier block to unregister | |
483 | * | |
484 | * @return 0 on success, otherwise a negative error code | |
485 | * | |
486 | * Register a notifier to get notified when backlight devices get registered | |
487 | * or unregistered. | |
488 | */ | |
489 | int backlight_unregister_notifier(struct notifier_block *nb) | |
490 | { | |
491 | return blocking_notifier_chain_unregister(&backlight_notifier, nb); | |
492 | } | |
493 | EXPORT_SYMBOL(backlight_unregister_notifier); | |
494 | ||
8318fde4 JH |
495 | /** |
496 | * devm_backlight_device_register - resource managed backlight_device_register() | |
497 | * @dev: the device to register | |
498 | * @name: the name of the device | |
499 | * @parent: a pointer to the parent device | |
500 | * @devdata: an optional pointer to be stored for private driver use | |
501 | * @ops: the backlight operations structure | |
502 | * @props: the backlight properties | |
503 | * | |
504 | * @return a struct backlight on success, or an ERR_PTR on error | |
505 | * | |
506 | * Managed backlight_device_register(). The backlight_device returned | |
507 | * from this function are automatically freed on driver detach. | |
508 | * See backlight_device_register() for more information. | |
509 | */ | |
510 | struct backlight_device *devm_backlight_device_register(struct device *dev, | |
511 | const char *name, struct device *parent, void *devdata, | |
512 | const struct backlight_ops *ops, | |
513 | const struct backlight_properties *props) | |
514 | { | |
515 | struct backlight_device **ptr, *backlight; | |
516 | ||
517 | ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr), | |
518 | GFP_KERNEL); | |
519 | if (!ptr) | |
520 | return ERR_PTR(-ENOMEM); | |
521 | ||
522 | backlight = backlight_device_register(name, parent, devdata, ops, | |
523 | props); | |
524 | if (!IS_ERR(backlight)) { | |
525 | *ptr = backlight; | |
526 | devres_add(dev, ptr); | |
527 | } else { | |
528 | devres_free(ptr); | |
529 | } | |
530 | ||
531 | return backlight; | |
532 | } | |
533 | EXPORT_SYMBOL(devm_backlight_device_register); | |
534 | ||
535 | /** | |
536 | * devm_backlight_device_unregister - resource managed backlight_device_unregister() | |
537 | * @dev: the device to unregister | |
538 | * @bd: the backlight device to unregister | |
539 | * | |
540 | * Deallocated a backlight allocated with devm_backlight_device_register(). | |
541 | * Normally this function will not need to be called and the resource management | |
542 | * code will ensure that the resource is freed. | |
543 | */ | |
544 | void devm_backlight_device_unregister(struct device *dev, | |
545 | struct backlight_device *bd) | |
546 | { | |
547 | int rc; | |
548 | ||
549 | rc = devres_release(dev, devm_backlight_device_release, | |
550 | devm_backlight_device_match, bd); | |
551 | WARN_ON(rc); | |
552 | } | |
553 | EXPORT_SYMBOL(devm_backlight_device_unregister); | |
554 | ||
762a936f | 555 | #ifdef CONFIG_OF |
3213f631 | 556 | static int of_parent_match(struct device *dev, const void *data) |
762a936f TR |
557 | { |
558 | return dev->parent && dev->parent->of_node == data; | |
559 | } | |
560 | ||
561 | /** | |
562 | * of_find_backlight_by_node() - find backlight device by device-tree node | |
563 | * @node: device-tree node of the backlight device | |
564 | * | |
565 | * Returns a pointer to the backlight device corresponding to the given DT | |
566 | * node or NULL if no such backlight device exists or if the device hasn't | |
567 | * been probed yet. | |
568 | * | |
569 | * This function obtains a reference on the backlight device and it is the | |
570 | * caller's responsibility to drop the reference by calling put_device() on | |
571 | * the backlight device's .dev field. | |
572 | */ | |
573 | struct backlight_device *of_find_backlight_by_node(struct device_node *node) | |
574 | { | |
575 | struct device *dev; | |
576 | ||
577 | dev = class_find_device(backlight_class, NULL, node, of_parent_match); | |
578 | ||
579 | return dev ? to_backlight_device(dev) : NULL; | |
580 | } | |
581 | EXPORT_SYMBOL(of_find_backlight_by_node); | |
582 | #endif | |
583 | ||
c2adda27 MM |
584 | /** |
585 | * of_find_backlight - Get backlight device | |
586 | * @dev: Device | |
587 | * | |
588 | * This function looks for a property named 'backlight' on the DT node | |
589 | * connected to @dev and looks up the backlight device. | |
590 | * | |
591 | * Call backlight_put() to drop the reference on the backlight device. | |
592 | * | |
593 | * Returns: | |
594 | * A pointer to the backlight device if found. | |
595 | * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight | |
596 | * device is found. | |
597 | * NULL if there's no backlight property. | |
598 | */ | |
599 | struct backlight_device *of_find_backlight(struct device *dev) | |
600 | { | |
601 | struct backlight_device *bd = NULL; | |
602 | struct device_node *np; | |
603 | ||
604 | if (!dev) | |
605 | return NULL; | |
606 | ||
607 | if (IS_ENABLED(CONFIG_OF) && dev->of_node) { | |
608 | np = of_parse_phandle(dev->of_node, "backlight", 0); | |
609 | if (np) { | |
610 | bd = of_find_backlight_by_node(np); | |
611 | of_node_put(np); | |
612 | if (!bd) | |
613 | return ERR_PTR(-EPROBE_DEFER); | |
614 | /* | |
615 | * Note: gpio_backlight uses brightness as | |
616 | * power state during probe | |
617 | */ | |
618 | if (!bd->props.brightness) | |
619 | bd->props.brightness = bd->props.max_brightness; | |
620 | } | |
621 | } | |
622 | ||
623 | return bd; | |
624 | } | |
625 | EXPORT_SYMBOL(of_find_backlight); | |
626 | ||
2e4ef334 MM |
627 | static void devm_backlight_release(void *data) |
628 | { | |
629 | backlight_put(data); | |
630 | } | |
631 | ||
632 | /** | |
633 | * devm_of_find_backlight - Resource-managed of_find_backlight() | |
634 | * @dev: Device | |
635 | * | |
636 | * Device managed version of of_find_backlight(). | |
637 | * The reference on the backlight device is automatically | |
638 | * dropped on driver detach. | |
639 | */ | |
640 | struct backlight_device *devm_of_find_backlight(struct device *dev) | |
641 | { | |
642 | struct backlight_device *bd; | |
643 | int ret; | |
644 | ||
645 | bd = of_find_backlight(dev); | |
646 | if (IS_ERR_OR_NULL(bd)) | |
647 | return bd; | |
648 | ret = devm_add_action(dev, devm_backlight_release, bd); | |
649 | if (ret) { | |
650 | backlight_put(bd); | |
651 | return ERR_PTR(ret); | |
652 | } | |
653 | return bd; | |
654 | } | |
655 | EXPORT_SYMBOL(devm_of_find_backlight); | |
656 | ||
1da177e4 LT |
657 | static void __exit backlight_class_exit(void) |
658 | { | |
655bfd7a | 659 | class_destroy(backlight_class); |
1da177e4 LT |
660 | } |
661 | ||
662 | static int __init backlight_class_init(void) | |
663 | { | |
655bfd7a RP |
664 | backlight_class = class_create(THIS_MODULE, "backlight"); |
665 | if (IS_ERR(backlight_class)) { | |
35f96162 JH |
666 | pr_warn("Unable to create backlight class; errno = %ld\n", |
667 | PTR_ERR(backlight_class)); | |
655bfd7a RP |
668 | return PTR_ERR(backlight_class); |
669 | } | |
670 | ||
ea1bb706 | 671 | backlight_class->dev_groups = bl_device_groups; |
3601792e | 672 | backlight_class->pm = &backlight_class_dev_pm_ops; |
5915a3db AL |
673 | INIT_LIST_HEAD(&backlight_dev_list); |
674 | mutex_init(&backlight_dev_list_mutex); | |
3cc6919b HG |
675 | BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier); |
676 | ||
655bfd7a | 677 | return 0; |
1da177e4 LT |
678 | } |
679 | ||
680 | /* | |
681 | * if this is compiled into the kernel, we need to ensure that the | |
682 | * class is registered before users of the class try to register lcd's | |
683 | */ | |
684 | postcore_initcall(backlight_class_init); | |
685 | module_exit(backlight_class_exit); | |
686 | ||
687 | MODULE_LICENSE("GPL"); | |
688 | MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>"); | |
689 | MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction"); |