| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Backlight Lowlevel Control Abstraction |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 21 | 22 | #include <asm/backlight.h> |
|---|
| 22 | 23 | #endif |
|---|
| 23 | 24 | |
|---|
| 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 | + |
|---|
| 24 | 66 | static struct list_head backlight_dev_list; |
|---|
| 25 | 67 | static struct mutex backlight_dev_list_mutex; |
|---|
| 26 | 68 | static struct blocking_notifier_head backlight_notifier; |
|---|
| .. | .. |
|---|
| 31 | 73 | [BACKLIGHT_FIRMWARE] = "firmware", |
|---|
| 32 | 74 | }; |
|---|
| 33 | 75 | |
|---|
| 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 | + |
|---|
| 34 | 82 | #if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \ |
|---|
| 35 | 83 | defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)) |
|---|
| 36 | | -/* This callback gets called when something important happens inside a |
|---|
| 37 | | - * framebuffer driver. We're looking if that important event is blanking, |
|---|
| 38 | | - * and if it is and necessary, we're switching backlight power as well ... |
|---|
| 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. |
|---|
| 39 | 95 | */ |
|---|
| 40 | 96 | static int fb_notifier_callback(struct notifier_block *self, |
|---|
| 41 | 97 | unsigned long event, void *data) |
|---|
| .. | .. |
|---|
| 46 | 102 | int fb_blank = 0; |
|---|
| 47 | 103 | |
|---|
| 48 | 104 | /* If we aren't interested in this event, skip it immediately ... */ |
|---|
| 49 | | - if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK) |
|---|
| 105 | + if (event != FB_EVENT_BLANK) |
|---|
| 50 | 106 | return 0; |
|---|
| 51 | 107 | |
|---|
| 52 | 108 | bd = container_of(self, struct backlight_device, fb_notif); |
|---|
| 53 | 109 | mutex_lock(&bd->ops_lock); |
|---|
| 54 | | - if (bd->ops) |
|---|
| 55 | | - if (!bd->ops->check_fb || |
|---|
| 56 | | - bd->ops->check_fb(bd, evdata->info)) { |
|---|
| 57 | | - fb_blank = *(int *)evdata->data; |
|---|
| 58 | | - if (fb_blank == FB_BLANK_UNBLANK && |
|---|
| 59 | | - !bd->fb_bl_on[node]) { |
|---|
| 60 | | - bd->fb_bl_on[node] = true; |
|---|
| 61 | | - if (!bd->use_count++) { |
|---|
| 62 | | - bd->props.state &= ~BL_CORE_FBBLANK; |
|---|
| 63 | | - bd->props.fb_blank = FB_BLANK_UNBLANK; |
|---|
| 64 | | - backlight_update_status(bd); |
|---|
| 65 | | - } |
|---|
| 66 | | - } else if (fb_blank != FB_BLANK_UNBLANK && |
|---|
| 67 | | - bd->fb_bl_on[node]) { |
|---|
| 68 | | - bd->fb_bl_on[node] = false; |
|---|
| 69 | | - if (!(--bd->use_count)) { |
|---|
| 70 | | - bd->props.state |= BL_CORE_FBBLANK; |
|---|
| 71 | | - bd->props.fb_blank = fb_blank; |
|---|
| 72 | | - backlight_update_status(bd); |
|---|
| 73 | | - } |
|---|
| 74 | | - } |
|---|
| 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); |
|---|
| 75 | 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); |
|---|
| 130 | + } |
|---|
| 131 | + } |
|---|
| 132 | +out: |
|---|
| 76 | 133 | mutex_unlock(&bd->ops_lock); |
|---|
| 77 | 134 | return 0; |
|---|
| 78 | 135 | } |
|---|
| .. | .. |
|---|
| 245 | 302 | } |
|---|
| 246 | 303 | static DEVICE_ATTR_RO(actual_brightness); |
|---|
| 247 | 304 | |
|---|
| 305 | +static ssize_t scale_show(struct device *dev, |
|---|
| 306 | + struct device_attribute *attr, char *buf) |
|---|
| 307 | +{ |
|---|
| 308 | + struct backlight_device *bd = to_backlight_device(dev); |
|---|
| 309 | + |
|---|
| 310 | + if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR)) |
|---|
| 311 | + return sprintf(buf, "unknown\n"); |
|---|
| 312 | + |
|---|
| 313 | + return sprintf(buf, "%s\n", backlight_scale_types[bd->props.scale]); |
|---|
| 314 | +} |
|---|
| 315 | +static DEVICE_ATTR_RO(scale); |
|---|
| 316 | + |
|---|
| 248 | 317 | static struct class *backlight_class; |
|---|
| 249 | 318 | |
|---|
| 250 | 319 | #ifdef CONFIG_PM_SLEEP |
|---|
| .. | .. |
|---|
| 291 | 360 | &dev_attr_brightness.attr, |
|---|
| 292 | 361 | &dev_attr_actual_brightness.attr, |
|---|
| 293 | 362 | &dev_attr_max_brightness.attr, |
|---|
| 363 | + &dev_attr_scale.attr, |
|---|
| 294 | 364 | &dev_attr_type.attr, |
|---|
| 295 | 365 | NULL, |
|---|
| 296 | 366 | }; |
|---|
| .. | .. |
|---|
| 300 | 370 | * backlight_force_update - tell the backlight subsystem that hardware state |
|---|
| 301 | 371 | * has changed |
|---|
| 302 | 372 | * @bd: the backlight device to update |
|---|
| 373 | + * @reason: reason for update |
|---|
| 303 | 374 | * |
|---|
| 304 | 375 | * Updates the internal state of the backlight in response to a hardware event, |
|---|
| 305 | | - * and generate a uevent to notify userspace |
|---|
| 376 | + * and generates an uevent to notify userspace. A backlight driver shall call |
|---|
| 377 | + * backlight_force_update() when the backlight is changed using, for example, |
|---|
| 378 | + * a hot-key. The updated brightness is read using get_brightness() and the |
|---|
| 379 | + * brightness value is reported using an uevent. |
|---|
| 306 | 380 | */ |
|---|
| 307 | 381 | void backlight_force_update(struct backlight_device *bd, |
|---|
| 308 | 382 | enum backlight_update_reason reason) |
|---|
| .. | .. |
|---|
| 315 | 389 | } |
|---|
| 316 | 390 | EXPORT_SYMBOL(backlight_force_update); |
|---|
| 317 | 391 | |
|---|
| 318 | | -/** |
|---|
| 319 | | - * backlight_device_register - create and register a new object of |
|---|
| 320 | | - * backlight_device class. |
|---|
| 321 | | - * @name: the name of the new object(must be the same as the name of the |
|---|
| 322 | | - * respective framebuffer device). |
|---|
| 323 | | - * @parent: a pointer to the parent device |
|---|
| 324 | | - * @devdata: an optional pointer to be stored for private driver use. The |
|---|
| 325 | | - * methods may retrieve it by using bl_get_data(bd). |
|---|
| 326 | | - * @ops: the backlight operations structure. |
|---|
| 327 | | - * |
|---|
| 328 | | - * Creates and registers new backlight device. Returns either an |
|---|
| 329 | | - * ERR_PTR() or a pointer to the newly allocated device. |
|---|
| 330 | | - */ |
|---|
| 392 | +/* deprecated - use devm_backlight_device_register() */ |
|---|
| 331 | 393 | struct backlight_device *backlight_device_register(const char *name, |
|---|
| 332 | 394 | struct device *parent, void *devdata, const struct backlight_ops *ops, |
|---|
| 333 | 395 | const struct backlight_properties *props) |
|---|
| .. | .. |
|---|
| 394 | 456 | } |
|---|
| 395 | 457 | EXPORT_SYMBOL(backlight_device_register); |
|---|
| 396 | 458 | |
|---|
| 459 | +/** backlight_device_get_by_type - find first backlight device of a type |
|---|
| 460 | + * @type: the type of backlight device |
|---|
| 461 | + * |
|---|
| 462 | + * Look up the first backlight device of the specified type |
|---|
| 463 | + * |
|---|
| 464 | + * RETURNS: |
|---|
| 465 | + * |
|---|
| 466 | + * Pointer to backlight device if any was found. Otherwise NULL. |
|---|
| 467 | + */ |
|---|
| 397 | 468 | struct backlight_device *backlight_device_get_by_type(enum backlight_type type) |
|---|
| 398 | 469 | { |
|---|
| 399 | 470 | bool found = false; |
|---|
| .. | .. |
|---|
| 413 | 484 | EXPORT_SYMBOL(backlight_device_get_by_type); |
|---|
| 414 | 485 | |
|---|
| 415 | 486 | /** |
|---|
| 416 | | - * backlight_device_unregister - unregisters a backlight device object. |
|---|
| 417 | | - * @bd: the backlight device object to be unregistered and freed. |
|---|
| 487 | + * backlight_device_get_by_name - Get backlight device by name |
|---|
| 488 | + * @name: Device name |
|---|
| 418 | 489 | * |
|---|
| 419 | | - * Unregisters a previously registered via backlight_device_register object. |
|---|
| 490 | + * This function looks up a backlight device by its name. It obtains a reference |
|---|
| 491 | + * on the backlight device and it is the caller's responsibility to drop the |
|---|
| 492 | + * reference by calling backlight_put(). |
|---|
| 493 | + * |
|---|
| 494 | + * Returns: |
|---|
| 495 | + * A pointer to the backlight device if found, otherwise NULL. |
|---|
| 420 | 496 | */ |
|---|
| 497 | +struct backlight_device *backlight_device_get_by_name(const char *name) |
|---|
| 498 | +{ |
|---|
| 499 | + struct device *dev; |
|---|
| 500 | + |
|---|
| 501 | + dev = class_find_device_by_name(backlight_class, name); |
|---|
| 502 | + |
|---|
| 503 | + return dev ? to_backlight_device(dev) : NULL; |
|---|
| 504 | +} |
|---|
| 505 | +EXPORT_SYMBOL(backlight_device_get_by_name); |
|---|
| 506 | + |
|---|
| 507 | +/* deprecated - use devm_backlight_device_unregister() */ |
|---|
| 421 | 508 | void backlight_device_unregister(struct backlight_device *bd) |
|---|
| 422 | 509 | { |
|---|
| 423 | 510 | if (!bd) |
|---|
| .. | .. |
|---|
| 465 | 552 | * backlight_register_notifier - get notified of backlight (un)registration |
|---|
| 466 | 553 | * @nb: notifier block with the notifier to call on backlight (un)registration |
|---|
| 467 | 554 | * |
|---|
| 468 | | - * @return 0 on success, otherwise a negative error code |
|---|
| 469 | | - * |
|---|
| 470 | 555 | * Register a notifier to get notified when backlight devices get registered |
|---|
| 471 | 556 | * or unregistered. |
|---|
| 557 | + * |
|---|
| 558 | + * RETURNS: |
|---|
| 559 | + * |
|---|
| 560 | + * 0 on success, otherwise a negative error code |
|---|
| 472 | 561 | */ |
|---|
| 473 | 562 | int backlight_register_notifier(struct notifier_block *nb) |
|---|
| 474 | 563 | { |
|---|
| .. | .. |
|---|
| 480 | 569 | * backlight_unregister_notifier - unregister a backlight notifier |
|---|
| 481 | 570 | * @nb: notifier block to unregister |
|---|
| 482 | 571 | * |
|---|
| 483 | | - * @return 0 on success, otherwise a negative error code |
|---|
| 484 | | - * |
|---|
| 485 | 572 | * Register a notifier to get notified when backlight devices get registered |
|---|
| 486 | 573 | * or unregistered. |
|---|
| 574 | + * |
|---|
| 575 | + * RETURNS: |
|---|
| 576 | + * |
|---|
| 577 | + * 0 on success, otherwise a negative error code |
|---|
| 487 | 578 | */ |
|---|
| 488 | 579 | int backlight_unregister_notifier(struct notifier_block *nb) |
|---|
| 489 | 580 | { |
|---|
| .. | .. |
|---|
| 492 | 583 | EXPORT_SYMBOL(backlight_unregister_notifier); |
|---|
| 493 | 584 | |
|---|
| 494 | 585 | /** |
|---|
| 495 | | - * devm_backlight_device_register - resource managed backlight_device_register() |
|---|
| 586 | + * devm_backlight_device_register - register a new backlight device |
|---|
| 496 | 587 | * @dev: the device to register |
|---|
| 497 | 588 | * @name: the name of the device |
|---|
| 498 | | - * @parent: a pointer to the parent device |
|---|
| 589 | + * @parent: a pointer to the parent device (often the same as @dev) |
|---|
| 499 | 590 | * @devdata: an optional pointer to be stored for private driver use |
|---|
| 500 | 591 | * @ops: the backlight operations structure |
|---|
| 501 | 592 | * @props: the backlight properties |
|---|
| 502 | 593 | * |
|---|
| 503 | | - * @return a struct backlight on success, or an ERR_PTR on error |
|---|
| 594 | + * Creates and registers new backlight device. When a backlight device |
|---|
| 595 | + * is registered the configuration must be specified in the @props |
|---|
| 596 | + * parameter. See description of &backlight_properties. |
|---|
| 504 | 597 | * |
|---|
| 505 | | - * Managed backlight_device_register(). The backlight_device returned |
|---|
| 506 | | - * from this function are automatically freed on driver detach. |
|---|
| 507 | | - * See backlight_device_register() for more information. |
|---|
| 598 | + * RETURNS: |
|---|
| 599 | + * |
|---|
| 600 | + * struct backlight on success, or an ERR_PTR on error |
|---|
| 508 | 601 | */ |
|---|
| 509 | 602 | struct backlight_device *devm_backlight_device_register(struct device *dev, |
|---|
| 510 | 603 | const char *name, struct device *parent, void *devdata, |
|---|
| .. | .. |
|---|
| 532 | 625 | EXPORT_SYMBOL(devm_backlight_device_register); |
|---|
| 533 | 626 | |
|---|
| 534 | 627 | /** |
|---|
| 535 | | - * devm_backlight_device_unregister - resource managed backlight_device_unregister() |
|---|
| 628 | + * devm_backlight_device_unregister - unregister backlight device |
|---|
| 536 | 629 | * @dev: the device to unregister |
|---|
| 537 | 630 | * @bd: the backlight device to unregister |
|---|
| 538 | 631 | * |
|---|
| 539 | | - * Deallocated a backlight allocated with devm_backlight_device_register(). |
|---|
| 632 | + * Deallocates a backlight allocated with devm_backlight_device_register(). |
|---|
| 540 | 633 | * Normally this function will not need to be called and the resource management |
|---|
| 541 | | - * code will ensure that the resource is freed. |
|---|
| 634 | + * code will ensure that the resources are freed. |
|---|
| 542 | 635 | */ |
|---|
| 543 | 636 | void devm_backlight_device_unregister(struct device *dev, |
|---|
| 544 | 637 | struct backlight_device *bd) |
|---|
| .. | .. |
|---|
| 580 | 673 | EXPORT_SYMBOL(of_find_backlight_by_node); |
|---|
| 581 | 674 | #endif |
|---|
| 582 | 675 | |
|---|
| 583 | | -/** |
|---|
| 584 | | - * of_find_backlight - Get backlight device |
|---|
| 585 | | - * @dev: Device |
|---|
| 586 | | - * |
|---|
| 587 | | - * This function looks for a property named 'backlight' on the DT node |
|---|
| 588 | | - * connected to @dev and looks up the backlight device. |
|---|
| 589 | | - * |
|---|
| 590 | | - * Call backlight_put() to drop the reference on the backlight device. |
|---|
| 591 | | - * |
|---|
| 592 | | - * Returns: |
|---|
| 593 | | - * A pointer to the backlight device if found. |
|---|
| 594 | | - * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight |
|---|
| 595 | | - * device is found. |
|---|
| 596 | | - * NULL if there's no backlight property. |
|---|
| 597 | | - */ |
|---|
| 598 | | -struct backlight_device *of_find_backlight(struct device *dev) |
|---|
| 676 | +static struct backlight_device *of_find_backlight(struct device *dev) |
|---|
| 599 | 677 | { |
|---|
| 600 | 678 | struct backlight_device *bd = NULL; |
|---|
| 601 | 679 | struct device_node *np; |
|---|
| .. | .. |
|---|
| 615 | 693 | |
|---|
| 616 | 694 | return bd; |
|---|
| 617 | 695 | } |
|---|
| 618 | | -EXPORT_SYMBOL(of_find_backlight); |
|---|
| 619 | 696 | |
|---|
| 620 | 697 | static void devm_backlight_release(void *data) |
|---|
| 621 | 698 | { |
|---|
| 622 | | - backlight_put(data); |
|---|
| 699 | + struct backlight_device *bd = data; |
|---|
| 700 | + |
|---|
| 701 | + if (bd) |
|---|
| 702 | + put_device(&bd->dev); |
|---|
| 623 | 703 | } |
|---|
| 624 | 704 | |
|---|
| 625 | 705 | /** |
|---|
| 626 | | - * devm_of_find_backlight - Resource-managed of_find_backlight() |
|---|
| 627 | | - * @dev: Device |
|---|
| 706 | + * devm_of_find_backlight - find backlight for a device |
|---|
| 707 | + * @dev: the device |
|---|
| 628 | 708 | * |
|---|
| 629 | | - * Device managed version of of_find_backlight(). |
|---|
| 630 | | - * The reference on the backlight device is automatically |
|---|
| 709 | + * This function looks for a property named 'backlight' on the DT node |
|---|
| 710 | + * connected to @dev and looks up the backlight device. The lookup is |
|---|
| 711 | + * device managed so the reference to the backlight device is automatically |
|---|
| 631 | 712 | * dropped on driver detach. |
|---|
| 713 | + * |
|---|
| 714 | + * RETURNS: |
|---|
| 715 | + * |
|---|
| 716 | + * A pointer to the backlight device if found. |
|---|
| 717 | + * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight |
|---|
| 718 | + * device is found. NULL if there's no backlight property. |
|---|
| 632 | 719 | */ |
|---|
| 633 | 720 | struct backlight_device *devm_of_find_backlight(struct device *dev) |
|---|
| 634 | 721 | { |
|---|
| .. | .. |
|---|
| 640 | 727 | return bd; |
|---|
| 641 | 728 | ret = devm_add_action(dev, devm_backlight_release, bd); |
|---|
| 642 | 729 | if (ret) { |
|---|
| 643 | | - backlight_put(bd); |
|---|
| 730 | + put_device(&bd->dev); |
|---|
| 644 | 731 | return ERR_PTR(ret); |
|---|
| 645 | 732 | } |
|---|
| 646 | 733 | return bd; |
|---|