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 | |
a1230eb2 SR |
25 | /** |
26 | * DOC: overview | |
27 | * | |
28 | * The backlight core supports implementing backlight drivers. | |
29 | * | |
30 | * A backlight driver registers a driver using | |
31 | * devm_backlight_device_register(). The properties of the backlight | |
32 | * driver such as type and max_brightness must be specified. | |
33 | * When the core detect changes in for example brightness or power state | |
34 | * the update_status() operation is called. The backlight driver shall | |
35 | * implement this operation and use it to adjust backlight. | |
36 | * | |
37 | * Several sysfs attributes are provided by the backlight core:: | |
38 | * | |
39 | * - brightness R/W, set the requested brightness level | |
40 | * - actual_brightness RO, the brightness level used by the HW | |
41 | * - max_brightness RO, the maximum brightness level supported | |
42 | * | |
43 | * See Documentation/ABI/stable/sysfs-class-backlight for the full list. | |
44 | * | |
45 | * The backlight can be adjusted using the sysfs interface, and | |
46 | * the backlight driver may also support adjusting backlight using | |
47 | * a hot-key or some other platform or firmware specific way. | |
48 | * | |
49 | * The driver must implement the get_brightness() operation if | |
50 | * the HW do not support all the levels that can be specified in | |
51 | * brightness, thus providing user-space access to the actual level | |
52 | * via the actual_brightness attribute. | |
53 | * | |
54 | * When the backlight changes this is reported to user-space using | |
55 | * an uevent connected to the actual_brightness attribute. | |
56 | * When brightness is set by platform specific means, for example | |
57 | * a hot-key to adjust backlight, the driver must notify the backlight | |
58 | * core that brightness has changed using backlight_force_update(). | |
59 | * | |
60 | * The backlight driver core receives notifications from fbdev and | |
61 | * if the event is FB_EVENT_BLANK and if the value of blank, from the | |
62 | * FBIOBLANK ioctrl, results in a change in the backlight state the | |
63 | * update_status() operation is called. | |
64 | */ | |
65 | ||
5915a3db AL |
66 | static struct list_head backlight_dev_list; |
67 | static struct mutex backlight_dev_list_mutex; | |
3cc6919b | 68 | static struct blocking_notifier_head backlight_notifier; |
5915a3db | 69 | |
c338bfb5 | 70 | static const char *const backlight_types[] = { |
bb7ca747 MG |
71 | [BACKLIGHT_RAW] = "raw", |
72 | [BACKLIGHT_PLATFORM] = "platform", | |
73 | [BACKLIGHT_FIRMWARE] = "firmware", | |
74 | }; | |
75 | ||
d55c028f MK |
76 | static const char *const backlight_scale_types[] = { |
77 | [BACKLIGHT_SCALE_UNKNOWN] = "unknown", | |
78 | [BACKLIGHT_SCALE_LINEAR] = "linear", | |
79 | [BACKLIGHT_SCALE_NON_LINEAR] = "non-linear", | |
80 | }; | |
81 | ||
3d5eeadd JS |
82 | #if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \ |
83 | defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)) | |
a1230eb2 SR |
84 | /* |
85 | * fb_notifier_callback | |
86 | * | |
87 | * This callback gets called when something important happens inside a | |
88 | * framebuffer driver. The backlight core only cares about FB_BLANK_UNBLANK | |
89 | * which is reported to the driver using backlight_update_status() | |
90 | * as a state change. | |
91 | * | |
92 | * There may be several fbdev's connected to the backlight device, | |
93 | * in which case they are kept track of. A state change is only reported | |
94 | * if there is a change in backlight for the specified fbdev. | |
3d5eeadd JS |
95 | */ |
96 | static int fb_notifier_callback(struct notifier_block *self, | |
97 | unsigned long event, void *data) | |
98 | { | |
99 | struct backlight_device *bd; | |
100 | struct fb_event *evdata = data; | |
a55944ca LY |
101 | int node = evdata->info->node; |
102 | int fb_blank = 0; | |
3d5eeadd JS |
103 | |
104 | /* If we aren't interested in this event, skip it immediately ... */ | |
7a625549 | 105 | if (event != FB_EVENT_BLANK) |
3d5eeadd JS |
106 | return 0; |
107 | ||
108 | bd = container_of(self, struct backlight_device, fb_notif); | |
599a52d1 | 109 | mutex_lock(&bd->ops_lock); |
4edfe224 SR |
110 | |
111 | if (!bd->ops) | |
112 | goto out; | |
113 | if (bd->ops->check_fb && !bd->ops->check_fb(bd, evdata->info)) | |
114 | goto out; | |
115 | ||
116 | fb_blank = *(int *)evdata->data; | |
117 | if (fb_blank == FB_BLANK_UNBLANK && !bd->fb_bl_on[node]) { | |
118 | bd->fb_bl_on[node] = true; | |
119 | if (!bd->use_count++) { | |
120 | bd->props.state &= ~BL_CORE_FBBLANK; | |
121 | bd->props.fb_blank = FB_BLANK_UNBLANK; | |
122 | backlight_update_status(bd); | |
123 | } | |
124 | } else if (fb_blank != FB_BLANK_UNBLANK && bd->fb_bl_on[node]) { | |
125 | bd->fb_bl_on[node] = false; | |
126 | if (!(--bd->use_count)) { | |
127 | bd->props.state |= BL_CORE_FBBLANK; | |
128 | bd->props.fb_blank = fb_blank; | |
129 | backlight_update_status(bd); | |
3d5eeadd | 130 | } |
4edfe224 SR |
131 | } |
132 | out: | |
599a52d1 | 133 | mutex_unlock(&bd->ops_lock); |
3d5eeadd JS |
134 | return 0; |
135 | } | |
136 | ||
137 | static int backlight_register_fb(struct backlight_device *bd) | |
138 | { | |
139 | memset(&bd->fb_notif, 0, sizeof(bd->fb_notif)); | |
140 | bd->fb_notif.notifier_call = fb_notifier_callback; | |
141 | ||
142 | return fb_register_client(&bd->fb_notif); | |
143 | } | |
144 | ||
145 | static void backlight_unregister_fb(struct backlight_device *bd) | |
146 | { | |
147 | fb_unregister_client(&bd->fb_notif); | |
148 | } | |
149 | #else | |
150 | static inline int backlight_register_fb(struct backlight_device *bd) | |
151 | { | |
152 | return 0; | |
153 | } | |
154 | ||
155 | static inline void backlight_unregister_fb(struct backlight_device *bd) | |
156 | { | |
157 | } | |
158 | #endif /* CONFIG_FB */ | |
159 | ||
325253a6 MG |
160 | static void backlight_generate_event(struct backlight_device *bd, |
161 | enum backlight_update_reason reason) | |
162 | { | |
163 | char *envp[2]; | |
164 | ||
165 | switch (reason) { | |
166 | case BACKLIGHT_UPDATE_SYSFS: | |
167 | envp[0] = "SOURCE=sysfs"; | |
168 | break; | |
169 | case BACKLIGHT_UPDATE_HOTKEY: | |
170 | envp[0] = "SOURCE=hotkey"; | |
171 | break; | |
172 | default: | |
173 | envp[0] = "SOURCE=unknown"; | |
174 | break; | |
175 | } | |
176 | envp[1] = NULL; | |
177 | kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp); | |
89dfc28c | 178 | sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness"); |
325253a6 MG |
179 | } |
180 | ||
ea1bb706 GKH |
181 | static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr, |
182 | char *buf) | |
1da177e4 | 183 | { |
655bfd7a | 184 | struct backlight_device *bd = to_backlight_device(dev); |
1da177e4 | 185 | |
599a52d1 | 186 | return sprintf(buf, "%d\n", bd->props.power); |
1da177e4 LT |
187 | } |
188 | ||
ea1bb706 GKH |
189 | static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr, |
190 | const char *buf, size_t count) | |
1da177e4 | 191 | { |
9a2c61a9 | 192 | int rc; |
655bfd7a | 193 | struct backlight_device *bd = to_backlight_device(dev); |
7e715c2d | 194 | unsigned long power, old_power; |
1da177e4 | 195 | |
66655760 | 196 | rc = kstrtoul(buf, 0, &power); |
9a2c61a9 PM |
197 | if (rc) |
198 | return rc; | |
1da177e4 | 199 | |
9a2c61a9 | 200 | rc = -ENXIO; |
599a52d1 RP |
201 | mutex_lock(&bd->ops_lock); |
202 | if (bd->ops) { | |
35f96162 | 203 | pr_debug("set power to %lu\n", power); |
51552453 | 204 | if (bd->props.power != power) { |
7e715c2d | 205 | old_power = bd->props.power; |
51552453 | 206 | bd->props.power = power; |
7e715c2d SM |
207 | rc = backlight_update_status(bd); |
208 | if (rc) | |
209 | bd->props.power = old_power; | |
210 | else | |
211 | rc = count; | |
212 | } else { | |
213 | rc = count; | |
51552453 | 214 | } |
6ca01765 | 215 | } |
599a52d1 | 216 | mutex_unlock(&bd->ops_lock); |
1da177e4 LT |
217 | |
218 | return rc; | |
219 | } | |
ea1bb706 | 220 | static DEVICE_ATTR_RW(bl_power); |
1da177e4 | 221 | |
ea1bb706 | 222 | static ssize_t brightness_show(struct device *dev, |
655bfd7a | 223 | struct device_attribute *attr, char *buf) |
1da177e4 | 224 | { |
655bfd7a | 225 | struct backlight_device *bd = to_backlight_device(dev); |
1da177e4 | 226 | |
599a52d1 | 227 | return sprintf(buf, "%d\n", bd->props.brightness); |
1da177e4 LT |
228 | } |
229 | ||
f6a4790a AL |
230 | int backlight_device_set_brightness(struct backlight_device *bd, |
231 | unsigned long brightness) | |
1da177e4 | 232 | { |
f6a4790a | 233 | int rc = -ENXIO; |
1da177e4 | 234 | |
599a52d1 RP |
235 | mutex_lock(&bd->ops_lock); |
236 | if (bd->ops) { | |
237 | if (brightness > bd->props.max_brightness) | |
6ca01765 RP |
238 | rc = -EINVAL; |
239 | else { | |
35f96162 | 240 | pr_debug("set brightness to %lu\n", brightness); |
9be1df98 | 241 | bd->props.brightness = brightness; |
7e715c2d | 242 | rc = backlight_update_status(bd); |
6ca01765 RP |
243 | } |
244 | } | |
599a52d1 | 245 | mutex_unlock(&bd->ops_lock); |
1da177e4 | 246 | |
325253a6 MG |
247 | backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); |
248 | ||
1da177e4 LT |
249 | return rc; |
250 | } | |
f6a4790a AL |
251 | EXPORT_SYMBOL(backlight_device_set_brightness); |
252 | ||
253 | static ssize_t brightness_store(struct device *dev, | |
254 | struct device_attribute *attr, const char *buf, size_t count) | |
255 | { | |
256 | int rc; | |
257 | struct backlight_device *bd = to_backlight_device(dev); | |
258 | unsigned long brightness; | |
259 | ||
260 | rc = kstrtoul(buf, 0, &brightness); | |
261 | if (rc) | |
262 | return rc; | |
263 | ||
264 | rc = backlight_device_set_brightness(bd, brightness); | |
265 | ||
266 | return rc ? rc : count; | |
267 | } | |
ea1bb706 | 268 | static DEVICE_ATTR_RW(brightness); |
1da177e4 | 269 | |
ea1bb706 GKH |
270 | static ssize_t type_show(struct device *dev, struct device_attribute *attr, |
271 | char *buf) | |
bb7ca747 MG |
272 | { |
273 | struct backlight_device *bd = to_backlight_device(dev); | |
274 | ||
275 | return sprintf(buf, "%s\n", backlight_types[bd->props.type]); | |
276 | } | |
ea1bb706 | 277 | static DEVICE_ATTR_RO(type); |
bb7ca747 | 278 | |
ea1bb706 | 279 | static ssize_t max_brightness_show(struct device *dev, |
655bfd7a | 280 | struct device_attribute *attr, char *buf) |
1da177e4 | 281 | { |
655bfd7a | 282 | struct backlight_device *bd = to_backlight_device(dev); |
1da177e4 | 283 | |
599a52d1 | 284 | return sprintf(buf, "%d\n", bd->props.max_brightness); |
6ca01765 | 285 | } |
ea1bb706 | 286 | static DEVICE_ATTR_RO(max_brightness); |
6ca01765 | 287 | |
ea1bb706 | 288 | static ssize_t actual_brightness_show(struct device *dev, |
655bfd7a | 289 | struct device_attribute *attr, char *buf) |
6ca01765 RP |
290 | { |
291 | int rc = -ENXIO; | |
655bfd7a | 292 | struct backlight_device *bd = to_backlight_device(dev); |
6ca01765 | 293 | |
599a52d1 | 294 | mutex_lock(&bd->ops_lock); |
563edf85 TW |
295 | if (bd->ops && bd->ops->get_brightness) { |
296 | rc = bd->ops->get_brightness(bd); | |
297 | if (rc >= 0) | |
298 | rc = sprintf(buf, "%d\n", rc); | |
299 | } else { | |
b3de3402 | 300 | rc = sprintf(buf, "%d\n", bd->props.brightness); |
563edf85 | 301 | } |
599a52d1 | 302 | mutex_unlock(&bd->ops_lock); |
1da177e4 LT |
303 | |
304 | return rc; | |
305 | } | |
ea1bb706 | 306 | static DEVICE_ATTR_RO(actual_brightness); |
1da177e4 | 307 | |
d55c028f MK |
308 | static ssize_t scale_show(struct device *dev, |
309 | struct device_attribute *attr, char *buf) | |
310 | { | |
311 | struct backlight_device *bd = to_backlight_device(dev); | |
312 | ||
313 | if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR)) | |
314 | return sprintf(buf, "unknown\n"); | |
315 | ||
316 | return sprintf(buf, "%s\n", backlight_scale_types[bd->props.scale]); | |
317 | } | |
318 | static DEVICE_ATTR_RO(scale); | |
319 | ||
0ad90efd | 320 | static struct class *backlight_class; |
655bfd7a | 321 | |
3601792e SK |
322 | #ifdef CONFIG_PM_SLEEP |
323 | static int backlight_suspend(struct device *dev) | |
c835ee7f RP |
324 | { |
325 | struct backlight_device *bd = to_backlight_device(dev); | |
326 | ||
d1d73578 UKK |
327 | mutex_lock(&bd->ops_lock); |
328 | if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { | |
c835ee7f RP |
329 | bd->props.state |= BL_CORE_SUSPENDED; |
330 | backlight_update_status(bd); | |
c835ee7f | 331 | } |
d1d73578 | 332 | mutex_unlock(&bd->ops_lock); |
c835ee7f RP |
333 | |
334 | return 0; | |
335 | } | |
336 | ||
337 | static int backlight_resume(struct device *dev) | |
338 | { | |
339 | struct backlight_device *bd = to_backlight_device(dev); | |
340 | ||
d1d73578 UKK |
341 | mutex_lock(&bd->ops_lock); |
342 | if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { | |
c835ee7f RP |
343 | bd->props.state &= ~BL_CORE_SUSPENDED; |
344 | backlight_update_status(bd); | |
c835ee7f | 345 | } |
d1d73578 | 346 | mutex_unlock(&bd->ops_lock); |
c835ee7f RP |
347 | |
348 | return 0; | |
349 | } | |
3601792e SK |
350 | #endif |
351 | ||
352 | static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend, | |
353 | backlight_resume); | |
c835ee7f | 354 | |
655bfd7a | 355 | static void bl_device_release(struct device *dev) |
1da177e4 LT |
356 | { |
357 | struct backlight_device *bd = to_backlight_device(dev); | |
358 | kfree(bd); | |
359 | } | |
360 | ||
ea1bb706 GKH |
361 | static struct attribute *bl_device_attrs[] = { |
362 | &dev_attr_bl_power.attr, | |
363 | &dev_attr_brightness.attr, | |
364 | &dev_attr_actual_brightness.attr, | |
365 | &dev_attr_max_brightness.attr, | |
d55c028f | 366 | &dev_attr_scale.attr, |
ea1bb706 GKH |
367 | &dev_attr_type.attr, |
368 | NULL, | |
1da177e4 | 369 | }; |
ea1bb706 | 370 | ATTRIBUTE_GROUPS(bl_device); |
1da177e4 | 371 | |
325253a6 MG |
372 | /** |
373 | * backlight_force_update - tell the backlight subsystem that hardware state | |
374 | * has changed | |
375 | * @bd: the backlight device to update | |
6c05632d | 376 | * @reason: reason for update |
325253a6 MG |
377 | * |
378 | * Updates the internal state of the backlight in response to a hardware event, | |
a1230eb2 SR |
379 | * and generates an uevent to notify userspace. A backlight driver shall call |
380 | * backlight_force_update() when the backlight is changed using, for example, | |
381 | * a hot-key. The updated brightness is read using get_brightness() and the | |
382 | * brightness value is reported using an uevent. | |
325253a6 MG |
383 | */ |
384 | void backlight_force_update(struct backlight_device *bd, | |
385 | enum backlight_update_reason reason) | |
386 | { | |
563edf85 TW |
387 | int brightness; |
388 | ||
325253a6 | 389 | mutex_lock(&bd->ops_lock); |
563edf85 TW |
390 | if (bd->ops && bd->ops->get_brightness) { |
391 | brightness = bd->ops->get_brightness(bd); | |
392 | if (brightness >= 0) | |
393 | bd->props.brightness = brightness; | |
394 | else | |
395 | dev_err(&bd->dev, | |
396 | "Could not update brightness from device: %pe\n", | |
397 | ERR_PTR(brightness)); | |
398 | } | |
325253a6 MG |
399 | mutex_unlock(&bd->ops_lock); |
400 | backlight_generate_event(bd, reason); | |
401 | } | |
402 | EXPORT_SYMBOL(backlight_force_update); | |
403 | ||
a1230eb2 | 404 | /* deprecated - use devm_backlight_device_register() */ |
519ab5f2 | 405 | struct backlight_device *backlight_device_register(const char *name, |
a19a6ee6 MG |
406 | struct device *parent, void *devdata, const struct backlight_ops *ops, |
407 | const struct backlight_properties *props) | |
1da177e4 | 408 | { |
1da177e4 | 409 | struct backlight_device *new_bd; |
655bfd7a | 410 | int rc; |
1da177e4 | 411 | |
655bfd7a | 412 | pr_debug("backlight_device_register: name=%s\n", name); |
1da177e4 | 413 | |
599a52d1 | 414 | new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); |
90968e8e | 415 | if (!new_bd) |
10ad1b73 | 416 | return ERR_PTR(-ENOMEM); |
1da177e4 | 417 | |
28ee086d | 418 | mutex_init(&new_bd->update_lock); |
599a52d1 | 419 | mutex_init(&new_bd->ops_lock); |
1da177e4 | 420 | |
655bfd7a RP |
421 | new_bd->dev.class = backlight_class; |
422 | new_bd->dev.parent = parent; | |
423 | new_bd->dev.release = bl_device_release; | |
02aa2a37 | 424 | dev_set_name(&new_bd->dev, "%s", name); |
655bfd7a RP |
425 | dev_set_drvdata(&new_bd->dev, devdata); |
426 | ||
a19a6ee6 | 427 | /* Set default properties */ |
bb7ca747 | 428 | if (props) { |
a19a6ee6 MG |
429 | memcpy(&new_bd->props, props, |
430 | sizeof(struct backlight_properties)); | |
bb7ca747 MG |
431 | if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) { |
432 | WARN(1, "%s: invalid backlight type", name); | |
433 | new_bd->props.type = BACKLIGHT_RAW; | |
434 | } | |
435 | } else { | |
436 | new_bd->props.type = BACKLIGHT_RAW; | |
437 | } | |
a19a6ee6 | 438 | |
655bfd7a | 439 | rc = device_register(&new_bd->dev); |
90968e8e | 440 | if (rc) { |
35762a47 | 441 | put_device(&new_bd->dev); |
1da177e4 LT |
442 | return ERR_PTR(rc); |
443 | } | |
444 | ||
3d5eeadd | 445 | rc = backlight_register_fb(new_bd); |
2fd5a154 | 446 | if (rc) { |
655bfd7a | 447 | device_unregister(&new_bd->dev); |
2fd5a154 DT |
448 | return ERR_PTR(rc); |
449 | } | |
450 | ||
655bfd7a | 451 | new_bd->ops = ops; |
1da177e4 | 452 | |
321709c5 RP |
453 | #ifdef CONFIG_PMAC_BACKLIGHT |
454 | mutex_lock(&pmac_backlight_mutex); | |
455 | if (!pmac_backlight) | |
456 | pmac_backlight = new_bd; | |
457 | mutex_unlock(&pmac_backlight_mutex); | |
458 | #endif | |
459 | ||
5915a3db AL |
460 | mutex_lock(&backlight_dev_list_mutex); |
461 | list_add(&new_bd->entry, &backlight_dev_list); | |
462 | mutex_unlock(&backlight_dev_list_mutex); | |
463 | ||
3cc6919b HG |
464 | blocking_notifier_call_chain(&backlight_notifier, |
465 | BACKLIGHT_REGISTERED, new_bd); | |
466 | ||
1da177e4 LT |
467 | return new_bd; |
468 | } | |
469 | EXPORT_SYMBOL(backlight_device_register); | |
470 | ||
a1230eb2 SR |
471 | /** backlight_device_get_by_type - find first backlight device of a type |
472 | * @type: the type of backlight device | |
473 | * | |
474 | * Look up the first backlight device of the specified type | |
475 | * | |
476 | * RETURNS: | |
477 | * | |
478 | * Pointer to backlight device if any was found. Otherwise NULL. | |
479 | */ | |
f6a4790a | 480 | struct backlight_device *backlight_device_get_by_type(enum backlight_type type) |
5915a3db AL |
481 | { |
482 | bool found = false; | |
483 | struct backlight_device *bd; | |
484 | ||
485 | mutex_lock(&backlight_dev_list_mutex); | |
486 | list_for_each_entry(bd, &backlight_dev_list, entry) { | |
487 | if (bd->props.type == type) { | |
488 | found = true; | |
489 | break; | |
490 | } | |
491 | } | |
492 | mutex_unlock(&backlight_dev_list_mutex); | |
493 | ||
f6a4790a AL |
494 | return found ? bd : NULL; |
495 | } | |
496 | EXPORT_SYMBOL(backlight_device_get_by_type); | |
497 | ||
479da1f5 NT |
498 | /** |
499 | * backlight_device_get_by_name - Get backlight device by name | |
500 | * @name: Device name | |
501 | * | |
502 | * This function looks up a backlight device by its name. It obtains a reference | |
503 | * on the backlight device and it is the caller's responsibility to drop the | |
3755b46a | 504 | * reference by calling put_device(). |
479da1f5 NT |
505 | * |
506 | * Returns: | |
507 | * A pointer to the backlight device if found, otherwise NULL. | |
508 | */ | |
509 | struct backlight_device *backlight_device_get_by_name(const char *name) | |
510 | { | |
511 | struct device *dev; | |
512 | ||
513 | dev = class_find_device_by_name(backlight_class, name); | |
514 | ||
515 | return dev ? to_backlight_device(dev) : NULL; | |
516 | } | |
517 | EXPORT_SYMBOL(backlight_device_get_by_name); | |
518 | ||
a1230eb2 | 519 | /* deprecated - use devm_backlight_device_unregister() */ |
1da177e4 LT |
520 | void backlight_device_unregister(struct backlight_device *bd) |
521 | { | |
1da177e4 LT |
522 | if (!bd) |
523 | return; | |
524 | ||
5915a3db AL |
525 | mutex_lock(&backlight_dev_list_mutex); |
526 | list_del(&bd->entry); | |
527 | mutex_unlock(&backlight_dev_list_mutex); | |
528 | ||
321709c5 RP |
529 | #ifdef CONFIG_PMAC_BACKLIGHT |
530 | mutex_lock(&pmac_backlight_mutex); | |
531 | if (pmac_backlight == bd) | |
532 | pmac_backlight = NULL; | |
533 | mutex_unlock(&pmac_backlight_mutex); | |
534 | #endif | |
3cc6919b HG |
535 | |
536 | blocking_notifier_call_chain(&backlight_notifier, | |
537 | BACKLIGHT_UNREGISTERED, bd); | |
538 | ||
599a52d1 RP |
539 | mutex_lock(&bd->ops_lock); |
540 | bd->ops = NULL; | |
541 | mutex_unlock(&bd->ops_lock); | |
1da177e4 | 542 | |
3d5eeadd | 543 | backlight_unregister_fb(bd); |
655bfd7a | 544 | device_unregister(&bd->dev); |
1da177e4 LT |
545 | } |
546 | EXPORT_SYMBOL(backlight_device_unregister); | |
547 | ||
8318fde4 JH |
548 | static void devm_backlight_device_release(struct device *dev, void *res) |
549 | { | |
550 | struct backlight_device *backlight = *(struct backlight_device **)res; | |
551 | ||
552 | backlight_device_unregister(backlight); | |
553 | } | |
554 | ||
555 | static int devm_backlight_device_match(struct device *dev, void *res, | |
556 | void *data) | |
557 | { | |
558 | struct backlight_device **r = res; | |
559 | ||
560 | return *r == data; | |
561 | } | |
562 | ||
3cc6919b HG |
563 | /** |
564 | * backlight_register_notifier - get notified of backlight (un)registration | |
565 | * @nb: notifier block with the notifier to call on backlight (un)registration | |
566 | * | |
3cc6919b HG |
567 | * Register a notifier to get notified when backlight devices get registered |
568 | * or unregistered. | |
a1230eb2 SR |
569 | * |
570 | * RETURNS: | |
571 | * | |
572 | * 0 on success, otherwise a negative error code | |
3cc6919b HG |
573 | */ |
574 | int backlight_register_notifier(struct notifier_block *nb) | |
575 | { | |
576 | return blocking_notifier_chain_register(&backlight_notifier, nb); | |
577 | } | |
578 | EXPORT_SYMBOL(backlight_register_notifier); | |
579 | ||
580 | /** | |
581 | * backlight_unregister_notifier - unregister a backlight notifier | |
582 | * @nb: notifier block to unregister | |
583 | * | |
3cc6919b HG |
584 | * Register a notifier to get notified when backlight devices get registered |
585 | * or unregistered. | |
a1230eb2 SR |
586 | * |
587 | * RETURNS: | |
588 | * | |
589 | * 0 on success, otherwise a negative error code | |
3cc6919b HG |
590 | */ |
591 | int backlight_unregister_notifier(struct notifier_block *nb) | |
592 | { | |
593 | return blocking_notifier_chain_unregister(&backlight_notifier, nb); | |
594 | } | |
595 | EXPORT_SYMBOL(backlight_unregister_notifier); | |
596 | ||
8318fde4 | 597 | /** |
a1230eb2 | 598 | * devm_backlight_device_register - register a new backlight device |
8318fde4 JH |
599 | * @dev: the device to register |
600 | * @name: the name of the device | |
a1230eb2 | 601 | * @parent: a pointer to the parent device (often the same as @dev) |
8318fde4 JH |
602 | * @devdata: an optional pointer to be stored for private driver use |
603 | * @ops: the backlight operations structure | |
604 | * @props: the backlight properties | |
605 | * | |
a1230eb2 SR |
606 | * Creates and registers new backlight device. When a backlight device |
607 | * is registered the configuration must be specified in the @props | |
608 | * parameter. See description of &backlight_properties. | |
609 | * | |
610 | * RETURNS: | |
8318fde4 | 611 | * |
a1230eb2 | 612 | * struct backlight on success, or an ERR_PTR on error |
8318fde4 JH |
613 | */ |
614 | struct backlight_device *devm_backlight_device_register(struct device *dev, | |
615 | const char *name, struct device *parent, void *devdata, | |
616 | const struct backlight_ops *ops, | |
617 | const struct backlight_properties *props) | |
618 | { | |
619 | struct backlight_device **ptr, *backlight; | |
620 | ||
621 | ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr), | |
622 | GFP_KERNEL); | |
623 | if (!ptr) | |
624 | return ERR_PTR(-ENOMEM); | |
625 | ||
626 | backlight = backlight_device_register(name, parent, devdata, ops, | |
627 | props); | |
628 | if (!IS_ERR(backlight)) { | |
629 | *ptr = backlight; | |
630 | devres_add(dev, ptr); | |
631 | } else { | |
632 | devres_free(ptr); | |
633 | } | |
634 | ||
635 | return backlight; | |
636 | } | |
637 | EXPORT_SYMBOL(devm_backlight_device_register); | |
638 | ||
639 | /** | |
a1230eb2 | 640 | * devm_backlight_device_unregister - unregister backlight device |
8318fde4 JH |
641 | * @dev: the device to unregister |
642 | * @bd: the backlight device to unregister | |
643 | * | |
a1230eb2 | 644 | * Deallocates a backlight allocated with devm_backlight_device_register(). |
8318fde4 | 645 | * Normally this function will not need to be called and the resource management |
a1230eb2 | 646 | * code will ensure that the resources are freed. |
8318fde4 JH |
647 | */ |
648 | void devm_backlight_device_unregister(struct device *dev, | |
649 | struct backlight_device *bd) | |
650 | { | |
651 | int rc; | |
652 | ||
653 | rc = devres_release(dev, devm_backlight_device_release, | |
654 | devm_backlight_device_match, bd); | |
655 | WARN_ON(rc); | |
656 | } | |
657 | EXPORT_SYMBOL(devm_backlight_device_unregister); | |
658 | ||
762a936f | 659 | #ifdef CONFIG_OF |
3213f631 | 660 | static int of_parent_match(struct device *dev, const void *data) |
762a936f TR |
661 | { |
662 | return dev->parent && dev->parent->of_node == data; | |
663 | } | |
664 | ||
665 | /** | |
666 | * of_find_backlight_by_node() - find backlight device by device-tree node | |
667 | * @node: device-tree node of the backlight device | |
668 | * | |
669 | * Returns a pointer to the backlight device corresponding to the given DT | |
670 | * node or NULL if no such backlight device exists or if the device hasn't | |
671 | * been probed yet. | |
672 | * | |
673 | * This function obtains a reference on the backlight device and it is the | |
674 | * caller's responsibility to drop the reference by calling put_device() on | |
675 | * the backlight device's .dev field. | |
676 | */ | |
677 | struct backlight_device *of_find_backlight_by_node(struct device_node *node) | |
678 | { | |
679 | struct device *dev; | |
680 | ||
681 | dev = class_find_device(backlight_class, NULL, node, of_parent_match); | |
682 | ||
683 | return dev ? to_backlight_device(dev) : NULL; | |
684 | } | |
685 | EXPORT_SYMBOL(of_find_backlight_by_node); | |
686 | #endif | |
687 | ||
b6539a11 | 688 | static struct backlight_device *of_find_backlight(struct device *dev) |
c2adda27 MM |
689 | { |
690 | struct backlight_device *bd = NULL; | |
691 | struct device_node *np; | |
692 | ||
693 | if (!dev) | |
694 | return NULL; | |
695 | ||
696 | if (IS_ENABLED(CONFIG_OF) && dev->of_node) { | |
697 | np = of_parse_phandle(dev->of_node, "backlight", 0); | |
698 | if (np) { | |
699 | bd = of_find_backlight_by_node(np); | |
700 | of_node_put(np); | |
701 | if (!bd) | |
702 | return ERR_PTR(-EPROBE_DEFER); | |
c2adda27 MM |
703 | } |
704 | } | |
705 | ||
706 | return bd; | |
707 | } | |
c2adda27 | 708 | |
2e4ef334 MM |
709 | static void devm_backlight_release(void *data) |
710 | { | |
0f6a3256 SR |
711 | struct backlight_device *bd = data; |
712 | ||
023a8830 | 713 | put_device(&bd->dev); |
2e4ef334 MM |
714 | } |
715 | ||
716 | /** | |
a1230eb2 SR |
717 | * devm_of_find_backlight - find backlight for a device |
718 | * @dev: the device | |
2e4ef334 | 719 | * |
a1230eb2 SR |
720 | * This function looks for a property named 'backlight' on the DT node |
721 | * connected to @dev and looks up the backlight device. The lookup is | |
722 | * device managed so the reference to the backlight device is automatically | |
2e4ef334 | 723 | * dropped on driver detach. |
a1230eb2 SR |
724 | * |
725 | * RETURNS: | |
726 | * | |
727 | * A pointer to the backlight device if found. | |
728 | * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight | |
729 | * device is found. NULL if there's no backlight property. | |
2e4ef334 MM |
730 | */ |
731 | struct backlight_device *devm_of_find_backlight(struct device *dev) | |
732 | { | |
733 | struct backlight_device *bd; | |
734 | int ret; | |
735 | ||
736 | bd = of_find_backlight(dev); | |
737 | if (IS_ERR_OR_NULL(bd)) | |
738 | return bd; | |
023a8830 CJ |
739 | ret = devm_add_action_or_reset(dev, devm_backlight_release, bd); |
740 | if (ret) | |
2e4ef334 | 741 | return ERR_PTR(ret); |
023a8830 | 742 | |
2e4ef334 MM |
743 | return bd; |
744 | } | |
745 | EXPORT_SYMBOL(devm_of_find_backlight); | |
746 | ||
1da177e4 LT |
747 | static void __exit backlight_class_exit(void) |
748 | { | |
655bfd7a | 749 | class_destroy(backlight_class); |
1da177e4 LT |
750 | } |
751 | ||
752 | static int __init backlight_class_init(void) | |
753 | { | |
655bfd7a RP |
754 | backlight_class = class_create(THIS_MODULE, "backlight"); |
755 | if (IS_ERR(backlight_class)) { | |
35f96162 JH |
756 | pr_warn("Unable to create backlight class; errno = %ld\n", |
757 | PTR_ERR(backlight_class)); | |
655bfd7a RP |
758 | return PTR_ERR(backlight_class); |
759 | } | |
760 | ||
ea1bb706 | 761 | backlight_class->dev_groups = bl_device_groups; |
3601792e | 762 | backlight_class->pm = &backlight_class_dev_pm_ops; |
5915a3db AL |
763 | INIT_LIST_HEAD(&backlight_dev_list); |
764 | mutex_init(&backlight_dev_list_mutex); | |
3cc6919b HG |
765 | BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier); |
766 | ||
655bfd7a | 767 | return 0; |
1da177e4 LT |
768 | } |
769 | ||
770 | /* | |
771 | * if this is compiled into the kernel, we need to ensure that the | |
772 | * class is registered before users of the class try to register lcd's | |
773 | */ | |
774 | postcore_initcall(backlight_class_init); | |
775 | module_exit(backlight_class_exit); | |
776 | ||
777 | MODULE_LICENSE("GPL"); | |
778 | MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>"); | |
779 | MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction"); |