1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * Backlight Lowlevel Control Abstraction |
4 | * |
5 | * Copyright (C) 2003,2004 Hewlett-Packard Company |
6 | * |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
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/slab.h> |
19 | |
20 | #ifdef CONFIG_PMAC_BACKLIGHT |
21 | #include <asm/backlight.h> |
22 | #endif |
23 | |
24 | /** |
25 | * DOC: overview |
26 | * |
27 | * The backlight core supports implementing backlight drivers. |
28 | * |
29 | * A backlight driver registers a driver using |
30 | * devm_backlight_device_register(). The properties of the backlight |
31 | * driver such as type and max_brightness must be specified. |
32 | * When the core detect changes in for example brightness or power state |
33 | * the update_status() operation is called. The backlight driver shall |
34 | * implement this operation and use it to adjust backlight. |
35 | * |
36 | * Several sysfs attributes are provided by the backlight core:: |
37 | * |
38 | * - brightness R/W, set the requested brightness level |
39 | * - actual_brightness RO, the brightness level used by the HW |
40 | * - max_brightness RO, the maximum brightness level supported |
41 | * |
42 | * See Documentation/ABI/stable/sysfs-class-backlight for the full list. |
43 | * |
44 | * The backlight can be adjusted using the sysfs interface, and |
45 | * the backlight driver may also support adjusting backlight using |
46 | * a hot-key or some other platform or firmware specific way. |
47 | * |
48 | * The driver must implement the get_brightness() operation if |
49 | * the HW do not support all the levels that can be specified in |
50 | * brightness, thus providing user-space access to the actual level |
51 | * via the actual_brightness attribute. |
52 | * |
53 | * When the backlight changes this is reported to user-space using |
54 | * an uevent connected to the actual_brightness attribute. |
55 | * When brightness is set by platform specific means, for example |
56 | * a hot-key to adjust backlight, the driver must notify the backlight |
57 | * core that brightness has changed using backlight_force_update(). |
58 | * |
59 | * Display drives can control the backlight device's status using |
60 | * backlight_notify_blank() and backlight_notify_blank_all(). If this |
61 | * results in a change in the backlight state the functions call the |
62 | * update_status() operation. |
63 | */ |
64 | |
65 | static struct list_head backlight_dev_list; |
66 | static struct mutex backlight_dev_list_mutex; |
67 | |
68 | static const char *const backlight_types[] = { |
69 | [BACKLIGHT_RAW] = "raw", |
70 | [BACKLIGHT_PLATFORM] = "platform", |
71 | [BACKLIGHT_FIRMWARE] = "firmware", |
72 | }; |
73 | |
74 | static const char *const backlight_scale_types[] = { |
75 | [BACKLIGHT_SCALE_UNKNOWN] = "unknown", |
76 | [BACKLIGHT_SCALE_LINEAR] = "linear", |
77 | [BACKLIGHT_SCALE_NON_LINEAR] = "non-linear", |
78 | }; |
79 | |
80 | void backlight_notify_blank(struct backlight_device *bd, struct device *display_dev, |
81 | bool fb_on, bool prev_fb_on) |
82 | { |
83 | guard(mutex)(T: &bd->ops_lock); |
84 | |
85 | if (!bd->ops) |
86 | return; |
87 | if (bd->ops->controls_device && !bd->ops->controls_device(bd, display_dev)) |
88 | return; |
89 | |
90 | if (fb_on && (!prev_fb_on || !bd->use_count)) { |
91 | if (!bd->use_count++) { |
92 | bd->props.state &= ~BL_CORE_FBBLANK; |
93 | backlight_update_status(bd); |
94 | } |
95 | } else if (!fb_on && prev_fb_on && bd->use_count) { |
96 | if (!(--bd->use_count)) { |
97 | bd->props.state |= BL_CORE_FBBLANK; |
98 | backlight_update_status(bd); |
99 | } |
100 | } |
101 | } |
102 | EXPORT_SYMBOL(backlight_notify_blank); |
103 | |
104 | void backlight_notify_blank_all(struct device *display_dev, bool fb_on, bool prev_fb_on) |
105 | { |
106 | struct backlight_device *bd; |
107 | |
108 | guard(mutex)(T: &backlight_dev_list_mutex); |
109 | |
110 | list_for_each_entry(bd, &backlight_dev_list, entry) |
111 | backlight_notify_blank(bd, display_dev, fb_on, prev_fb_on); |
112 | } |
113 | EXPORT_SYMBOL(backlight_notify_blank_all); |
114 | |
115 | static void backlight_generate_event(struct backlight_device *bd, |
116 | enum backlight_update_reason reason) |
117 | { |
118 | char *envp[2]; |
119 | |
120 | switch (reason) { |
121 | case BACKLIGHT_UPDATE_SYSFS: |
122 | envp[0] = "SOURCE=sysfs"; |
123 | break; |
124 | case BACKLIGHT_UPDATE_HOTKEY: |
125 | envp[0] = "SOURCE=hotkey"; |
126 | break; |
127 | default: |
128 | envp[0] = "SOURCE=unknown"; |
129 | break; |
130 | } |
131 | envp[1] = NULL; |
132 | kobject_uevent_env(kobj: &bd->dev.kobj, action: KOBJ_CHANGE, envp); |
133 | sysfs_notify(kobj: &bd->dev.kobj, NULL, attr: "actual_brightness"); |
134 | } |
135 | |
136 | static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr, |
137 | char *buf) |
138 | { |
139 | struct backlight_device *bd = to_backlight_device(dev); |
140 | |
141 | return sprintf(buf, fmt: "%d\n", bd->props.power); |
142 | } |
143 | |
144 | static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr, |
145 | const char *buf, size_t count) |
146 | { |
147 | int rc; |
148 | struct backlight_device *bd = to_backlight_device(dev); |
149 | unsigned long power, old_power; |
150 | |
151 | rc = kstrtoul(s: buf, base: 0, res: &power); |
152 | if (rc) |
153 | return rc; |
154 | |
155 | rc = -ENXIO; |
156 | mutex_lock(&bd->ops_lock); |
157 | if (bd->ops) { |
158 | pr_debug("set power to %lu\n", power); |
159 | if (bd->props.power != power) { |
160 | old_power = bd->props.power; |
161 | bd->props.power = power; |
162 | rc = backlight_update_status(bd); |
163 | if (rc) |
164 | bd->props.power = old_power; |
165 | else |
166 | rc = count; |
167 | } else { |
168 | rc = count; |
169 | } |
170 | } |
171 | mutex_unlock(lock: &bd->ops_lock); |
172 | |
173 | return rc; |
174 | } |
175 | static DEVICE_ATTR_RW(bl_power); |
176 | |
177 | static ssize_t brightness_show(struct device *dev, |
178 | struct device_attribute *attr, char *buf) |
179 | { |
180 | struct backlight_device *bd = to_backlight_device(dev); |
181 | |
182 | return sprintf(buf, fmt: "%d\n", bd->props.brightness); |
183 | } |
184 | |
185 | int backlight_device_set_brightness(struct backlight_device *bd, |
186 | unsigned long brightness) |
187 | { |
188 | int rc = -ENXIO; |
189 | |
190 | mutex_lock(&bd->ops_lock); |
191 | if (bd->ops) { |
192 | if (brightness > bd->props.max_brightness) |
193 | rc = -EINVAL; |
194 | else { |
195 | pr_debug("set brightness to %lu\n", brightness); |
196 | bd->props.brightness = brightness; |
197 | rc = backlight_update_status(bd); |
198 | } |
199 | } |
200 | mutex_unlock(lock: &bd->ops_lock); |
201 | |
202 | backlight_generate_event(bd, reason: BACKLIGHT_UPDATE_SYSFS); |
203 | |
204 | return rc; |
205 | } |
206 | EXPORT_SYMBOL(backlight_device_set_brightness); |
207 | |
208 | static ssize_t brightness_store(struct device *dev, |
209 | struct device_attribute *attr, const char *buf, size_t count) |
210 | { |
211 | int rc; |
212 | struct backlight_device *bd = to_backlight_device(dev); |
213 | unsigned long brightness; |
214 | |
215 | rc = kstrtoul(s: buf, base: 0, res: &brightness); |
216 | if (rc) |
217 | return rc; |
218 | |
219 | rc = backlight_device_set_brightness(bd, brightness); |
220 | |
221 | return rc ? rc : count; |
222 | } |
223 | static DEVICE_ATTR_RW(brightness); |
224 | |
225 | static ssize_t type_show(struct device *dev, struct device_attribute *attr, |
226 | char *buf) |
227 | { |
228 | struct backlight_device *bd = to_backlight_device(dev); |
229 | |
230 | return sprintf(buf, fmt: "%s\n", backlight_types[bd->props.type]); |
231 | } |
232 | static DEVICE_ATTR_RO(type); |
233 | |
234 | static ssize_t max_brightness_show(struct device *dev, |
235 | struct device_attribute *attr, char *buf) |
236 | { |
237 | struct backlight_device *bd = to_backlight_device(dev); |
238 | |
239 | return sprintf(buf, fmt: "%d\n", bd->props.max_brightness); |
240 | } |
241 | static DEVICE_ATTR_RO(max_brightness); |
242 | |
243 | static ssize_t actual_brightness_show(struct device *dev, |
244 | struct device_attribute *attr, char *buf) |
245 | { |
246 | int rc = -ENXIO; |
247 | struct backlight_device *bd = to_backlight_device(dev); |
248 | |
249 | mutex_lock(&bd->ops_lock); |
250 | if (bd->ops && bd->ops->get_brightness) { |
251 | rc = bd->ops->get_brightness(bd); |
252 | if (rc >= 0) |
253 | rc = sprintf(buf, fmt: "%d\n", rc); |
254 | } else { |
255 | rc = sprintf(buf, fmt: "%d\n", bd->props.brightness); |
256 | } |
257 | mutex_unlock(lock: &bd->ops_lock); |
258 | |
259 | return rc; |
260 | } |
261 | static DEVICE_ATTR_RO(actual_brightness); |
262 | |
263 | static ssize_t scale_show(struct device *dev, |
264 | struct device_attribute *attr, char *buf) |
265 | { |
266 | struct backlight_device *bd = to_backlight_device(dev); |
267 | |
268 | if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR)) |
269 | return sprintf(buf, fmt: "unknown\n"); |
270 | |
271 | return sprintf(buf, fmt: "%s\n", backlight_scale_types[bd->props.scale]); |
272 | } |
273 | static DEVICE_ATTR_RO(scale); |
274 | |
275 | #ifdef CONFIG_PM_SLEEP |
276 | static int backlight_suspend(struct device *dev) |
277 | { |
278 | struct backlight_device *bd = to_backlight_device(dev); |
279 | |
280 | mutex_lock(&bd->ops_lock); |
281 | if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { |
282 | bd->props.state |= BL_CORE_SUSPENDED; |
283 | backlight_update_status(bd); |
284 | } |
285 | mutex_unlock(lock: &bd->ops_lock); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static int backlight_resume(struct device *dev) |
291 | { |
292 | struct backlight_device *bd = to_backlight_device(dev); |
293 | |
294 | mutex_lock(&bd->ops_lock); |
295 | if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { |
296 | bd->props.state &= ~BL_CORE_SUSPENDED; |
297 | backlight_update_status(bd); |
298 | } |
299 | mutex_unlock(lock: &bd->ops_lock); |
300 | |
301 | return 0; |
302 | } |
303 | #endif |
304 | |
305 | static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend, |
306 | backlight_resume); |
307 | |
308 | static void bl_device_release(struct device *dev) |
309 | { |
310 | struct backlight_device *bd = to_backlight_device(dev); |
311 | kfree(objp: bd); |
312 | } |
313 | |
314 | static struct attribute *bl_device_attrs[] = { |
315 | &dev_attr_bl_power.attr, |
316 | &dev_attr_brightness.attr, |
317 | &dev_attr_actual_brightness.attr, |
318 | &dev_attr_max_brightness.attr, |
319 | &dev_attr_scale.attr, |
320 | &dev_attr_type.attr, |
321 | NULL, |
322 | }; |
323 | ATTRIBUTE_GROUPS(bl_device); |
324 | |
325 | static const struct class backlight_class = { |
326 | .name = "backlight", |
327 | .dev_groups = bl_device_groups, |
328 | .pm = &backlight_class_dev_pm_ops, |
329 | }; |
330 | |
331 | /** |
332 | * backlight_force_update - tell the backlight subsystem that hardware state |
333 | * has changed |
334 | * @bd: the backlight device to update |
335 | * @reason: reason for update |
336 | * |
337 | * Updates the internal state of the backlight in response to a hardware event, |
338 | * and generates an uevent to notify userspace. A backlight driver shall call |
339 | * backlight_force_update() when the backlight is changed using, for example, |
340 | * a hot-key. The updated brightness is read using get_brightness() and the |
341 | * brightness value is reported using an uevent. |
342 | */ |
343 | void backlight_force_update(struct backlight_device *bd, |
344 | enum backlight_update_reason reason) |
345 | { |
346 | int brightness; |
347 | |
348 | mutex_lock(&bd->ops_lock); |
349 | if (bd->ops && bd->ops->get_brightness) { |
350 | brightness = bd->ops->get_brightness(bd); |
351 | if (brightness >= 0) |
352 | bd->props.brightness = brightness; |
353 | else |
354 | dev_err(&bd->dev, |
355 | "Could not update brightness from device: %pe\n", |
356 | ERR_PTR(brightness)); |
357 | } |
358 | mutex_unlock(lock: &bd->ops_lock); |
359 | backlight_generate_event(bd, reason); |
360 | } |
361 | EXPORT_SYMBOL(backlight_force_update); |
362 | |
363 | /* deprecated - use devm_backlight_device_register() */ |
364 | struct backlight_device *backlight_device_register(const char *name, |
365 | struct device *parent, void *devdata, const struct backlight_ops *ops, |
366 | const struct backlight_properties *props) |
367 | { |
368 | struct backlight_device *new_bd; |
369 | int rc; |
370 | |
371 | pr_debug("backlight_device_register: name=%s\n", name); |
372 | |
373 | new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); |
374 | if (!new_bd) |
375 | return ERR_PTR(error: -ENOMEM); |
376 | |
377 | mutex_init(&new_bd->update_lock); |
378 | mutex_init(&new_bd->ops_lock); |
379 | |
380 | new_bd->dev.class = &backlight_class; |
381 | new_bd->dev.parent = parent; |
382 | new_bd->dev.release = bl_device_release; |
383 | dev_set_name(dev: &new_bd->dev, name: "%s", name); |
384 | dev_set_drvdata(dev: &new_bd->dev, data: devdata); |
385 | |
386 | /* Set default properties */ |
387 | if (props) { |
388 | memcpy(&new_bd->props, props, |
389 | sizeof(struct backlight_properties)); |
390 | if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) { |
391 | WARN(1, "%s: invalid backlight type", name); |
392 | new_bd->props.type = BACKLIGHT_RAW; |
393 | } |
394 | } else { |
395 | new_bd->props.type = BACKLIGHT_RAW; |
396 | } |
397 | |
398 | rc = device_register(dev: &new_bd->dev); |
399 | if (rc) { |
400 | put_device(dev: &new_bd->dev); |
401 | return ERR_PTR(error: rc); |
402 | } |
403 | |
404 | new_bd->ops = ops; |
405 | |
406 | #ifdef CONFIG_PMAC_BACKLIGHT |
407 | mutex_lock(&pmac_backlight_mutex); |
408 | if (!pmac_backlight) |
409 | pmac_backlight = new_bd; |
410 | mutex_unlock(&pmac_backlight_mutex); |
411 | #endif |
412 | |
413 | mutex_lock(&backlight_dev_list_mutex); |
414 | list_add(new: &new_bd->entry, head: &backlight_dev_list); |
415 | mutex_unlock(lock: &backlight_dev_list_mutex); |
416 | |
417 | return new_bd; |
418 | } |
419 | EXPORT_SYMBOL(backlight_device_register); |
420 | |
421 | /** backlight_device_get_by_type - find first backlight device of a type |
422 | * @type: the type of backlight device |
423 | * |
424 | * Look up the first backlight device of the specified type |
425 | * |
426 | * RETURNS: |
427 | * |
428 | * Pointer to backlight device if any was found. Otherwise NULL. |
429 | */ |
430 | struct backlight_device *backlight_device_get_by_type(enum backlight_type type) |
431 | { |
432 | bool found = false; |
433 | struct backlight_device *bd; |
434 | |
435 | mutex_lock(&backlight_dev_list_mutex); |
436 | list_for_each_entry(bd, &backlight_dev_list, entry) { |
437 | if (bd->props.type == type) { |
438 | found = true; |
439 | break; |
440 | } |
441 | } |
442 | mutex_unlock(lock: &backlight_dev_list_mutex); |
443 | |
444 | return found ? bd : NULL; |
445 | } |
446 | EXPORT_SYMBOL(backlight_device_get_by_type); |
447 | |
448 | /** |
449 | * backlight_device_get_by_name - Get backlight device by name |
450 | * @name: Device name |
451 | * |
452 | * This function looks up a backlight device by its name. It obtains a reference |
453 | * on the backlight device and it is the caller's responsibility to drop the |
454 | * reference by calling put_device(). |
455 | * |
456 | * Returns: |
457 | * A pointer to the backlight device if found, otherwise NULL. |
458 | */ |
459 | struct backlight_device *backlight_device_get_by_name(const char *name) |
460 | { |
461 | struct device *dev; |
462 | |
463 | dev = class_find_device_by_name(class: &backlight_class, name); |
464 | |
465 | return dev ? to_backlight_device(dev) : NULL; |
466 | } |
467 | EXPORT_SYMBOL(backlight_device_get_by_name); |
468 | |
469 | /* deprecated - use devm_backlight_device_unregister() */ |
470 | void backlight_device_unregister(struct backlight_device *bd) |
471 | { |
472 | if (!bd) |
473 | return; |
474 | |
475 | mutex_lock(&backlight_dev_list_mutex); |
476 | list_del(entry: &bd->entry); |
477 | mutex_unlock(lock: &backlight_dev_list_mutex); |
478 | |
479 | #ifdef CONFIG_PMAC_BACKLIGHT |
480 | mutex_lock(&pmac_backlight_mutex); |
481 | if (pmac_backlight == bd) |
482 | pmac_backlight = NULL; |
483 | mutex_unlock(&pmac_backlight_mutex); |
484 | #endif |
485 | |
486 | mutex_lock(&bd->ops_lock); |
487 | bd->ops = NULL; |
488 | mutex_unlock(lock: &bd->ops_lock); |
489 | |
490 | device_unregister(dev: &bd->dev); |
491 | } |
492 | EXPORT_SYMBOL(backlight_device_unregister); |
493 | |
494 | static void devm_backlight_device_release(struct device *dev, void *res) |
495 | { |
496 | struct backlight_device *backlight = *(struct backlight_device **)res; |
497 | |
498 | backlight_device_unregister(backlight); |
499 | } |
500 | |
501 | static int devm_backlight_device_match(struct device *dev, void *res, |
502 | void *data) |
503 | { |
504 | struct backlight_device **r = res; |
505 | |
506 | return *r == data; |
507 | } |
508 | |
509 | /** |
510 | * devm_backlight_device_register - register a new backlight device |
511 | * @dev: the device to register |
512 | * @name: the name of the device |
513 | * @parent: a pointer to the parent device (often the same as @dev) |
514 | * @devdata: an optional pointer to be stored for private driver use |
515 | * @ops: the backlight operations structure |
516 | * @props: the backlight properties |
517 | * |
518 | * Creates and registers new backlight device. When a backlight device |
519 | * is registered the configuration must be specified in the @props |
520 | * parameter. See description of &backlight_properties. |
521 | * |
522 | * RETURNS: |
523 | * |
524 | * struct backlight on success, or an ERR_PTR on error |
525 | */ |
526 | struct backlight_device *devm_backlight_device_register(struct device *dev, |
527 | const char *name, struct device *parent, void *devdata, |
528 | const struct backlight_ops *ops, |
529 | const struct backlight_properties *props) |
530 | { |
531 | struct backlight_device **ptr, *backlight; |
532 | |
533 | ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr), |
534 | GFP_KERNEL); |
535 | if (!ptr) |
536 | return ERR_PTR(error: -ENOMEM); |
537 | |
538 | backlight = backlight_device_register(name, parent, devdata, ops, |
539 | props); |
540 | if (!IS_ERR(ptr: backlight)) { |
541 | *ptr = backlight; |
542 | devres_add(dev, res: ptr); |
543 | } else { |
544 | devres_free(res: ptr); |
545 | } |
546 | |
547 | return backlight; |
548 | } |
549 | EXPORT_SYMBOL(devm_backlight_device_register); |
550 | |
551 | /** |
552 | * devm_backlight_device_unregister - unregister backlight device |
553 | * @dev: the device to unregister |
554 | * @bd: the backlight device to unregister |
555 | * |
556 | * Deallocates a backlight allocated with devm_backlight_device_register(). |
557 | * Normally this function will not need to be called and the resource management |
558 | * code will ensure that the resources are freed. |
559 | */ |
560 | void devm_backlight_device_unregister(struct device *dev, |
561 | struct backlight_device *bd) |
562 | { |
563 | int rc; |
564 | |
565 | rc = devres_release(dev, release: devm_backlight_device_release, |
566 | match: devm_backlight_device_match, match_data: bd); |
567 | WARN_ON(rc); |
568 | } |
569 | EXPORT_SYMBOL(devm_backlight_device_unregister); |
570 | |
571 | #ifdef CONFIG_OF |
572 | static int of_parent_match(struct device *dev, const void *data) |
573 | { |
574 | return dev->parent && dev->parent->of_node == data; |
575 | } |
576 | |
577 | /** |
578 | * of_find_backlight_by_node() - find backlight device by device-tree node |
579 | * @node: device-tree node of the backlight device |
580 | * |
581 | * Returns a pointer to the backlight device corresponding to the given DT |
582 | * node or NULL if no such backlight device exists or if the device hasn't |
583 | * been probed yet. |
584 | * |
585 | * This function obtains a reference on the backlight device and it is the |
586 | * caller's responsibility to drop the reference by calling put_device() on |
587 | * the backlight device's .dev field. |
588 | */ |
589 | struct backlight_device *of_find_backlight_by_node(struct device_node *node) |
590 | { |
591 | struct device *dev; |
592 | |
593 | dev = class_find_device(class: &backlight_class, NULL, data: node, match: of_parent_match); |
594 | |
595 | return dev ? to_backlight_device(dev) : NULL; |
596 | } |
597 | EXPORT_SYMBOL(of_find_backlight_by_node); |
598 | #endif |
599 | |
600 | static struct backlight_device *of_find_backlight(struct device *dev) |
601 | { |
602 | struct backlight_device *bd = NULL; |
603 | struct device_node *np; |
604 | |
605 | if (!dev) |
606 | return NULL; |
607 | |
608 | if (IS_ENABLED(CONFIG_OF) && dev->of_node) { |
609 | np = of_parse_phandle(np: dev->of_node, phandle_name: "backlight", index: 0); |
610 | if (np) { |
611 | bd = of_find_backlight_by_node(np); |
612 | of_node_put(node: np); |
613 | if (!bd) |
614 | return ERR_PTR(error: -EPROBE_DEFER); |
615 | } |
616 | } |
617 | |
618 | return bd; |
619 | } |
620 | |
621 | static void devm_backlight_release(void *data) |
622 | { |
623 | struct backlight_device *bd = data; |
624 | |
625 | put_device(dev: &bd->dev); |
626 | } |
627 | |
628 | /** |
629 | * devm_of_find_backlight - find backlight for a device |
630 | * @dev: the device |
631 | * |
632 | * This function looks for a property named 'backlight' on the DT node |
633 | * connected to @dev and looks up the backlight device. The lookup is |
634 | * device managed so the reference to the backlight device is automatically |
635 | * dropped on driver detach. |
636 | * |
637 | * RETURNS: |
638 | * |
639 | * A pointer to the backlight device if found. |
640 | * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight |
641 | * device is found. NULL if there's no backlight property. |
642 | */ |
643 | struct backlight_device *devm_of_find_backlight(struct device *dev) |
644 | { |
645 | struct backlight_device *bd; |
646 | int ret; |
647 | |
648 | bd = of_find_backlight(dev); |
649 | if (IS_ERR_OR_NULL(ptr: bd)) |
650 | return bd; |
651 | ret = devm_add_action_or_reset(dev, devm_backlight_release, bd); |
652 | if (ret) |
653 | return ERR_PTR(error: ret); |
654 | |
655 | return bd; |
656 | } |
657 | EXPORT_SYMBOL(devm_of_find_backlight); |
658 | |
659 | static void __exit backlight_class_exit(void) |
660 | { |
661 | class_unregister(class: &backlight_class); |
662 | } |
663 | |
664 | static int __init backlight_class_init(void) |
665 | { |
666 | int ret; |
667 | |
668 | ret = class_register(class: &backlight_class); |
669 | if (ret) { |
670 | pr_warn("Unable to create backlight class; errno = %d\n", ret); |
671 | return ret; |
672 | } |
673 | |
674 | INIT_LIST_HEAD(list: &backlight_dev_list); |
675 | mutex_init(&backlight_dev_list_mutex); |
676 | |
677 | return 0; |
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"); |
690 |
Definitions
- backlight_dev_list
- backlight_dev_list_mutex
- backlight_types
- backlight_scale_types
- backlight_notify_blank
- backlight_notify_blank_all
- backlight_generate_event
- bl_power_show
- bl_power_store
- brightness_show
- backlight_device_set_brightness
- brightness_store
- type_show
- max_brightness_show
- actual_brightness_show
- scale_show
- backlight_suspend
- backlight_resume
- backlight_class_dev_pm_ops
- bl_device_release
- bl_device_attrs
- backlight_class
- backlight_force_update
- backlight_device_register
- backlight_device_get_by_type
- backlight_device_get_by_name
- backlight_device_unregister
- devm_backlight_device_release
- devm_backlight_device_match
- devm_backlight_device_register
- devm_backlight_device_unregister
- of_parent_match
- of_find_backlight_by_node
- of_find_backlight
- devm_backlight_release
- devm_of_find_backlight
- backlight_class_exit
Improve your Profiling and Debugging skills
Find out more