785 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Backlight Lowlevel Control Abstraction4 *5 * Copyright (C) 2003,2004 Hewlett-Packard Company6 *7 */8 9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt10 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>19#include <linux/slab.h>20 21#ifdef CONFIG_PMAC_BACKLIGHT22#include <asm/backlight.h>23#endif24 25/**26 * DOC: overview27 *28 * The backlight core supports implementing backlight drivers.29 *30 * A backlight driver registers a driver using31 * devm_backlight_device_register(). The properties of the backlight32 * driver such as type and max_brightness must be specified.33 * When the core detect changes in for example brightness or power state34 * the update_status() operation is called. The backlight driver shall35 * 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 level40 * - actual_brightness RO, the brightness level used by the HW41 * - max_brightness RO, the maximum brightness level supported42 *43 * See Documentation/ABI/stable/sysfs-class-backlight for the full list.44 *45 * The backlight can be adjusted using the sysfs interface, and46 * the backlight driver may also support adjusting backlight using47 * a hot-key or some other platform or firmware specific way.48 *49 * The driver must implement the get_brightness() operation if50 * the HW do not support all the levels that can be specified in51 * brightness, thus providing user-space access to the actual level52 * via the actual_brightness attribute.53 *54 * When the backlight changes this is reported to user-space using55 * an uevent connected to the actual_brightness attribute.56 * When brightness is set by platform specific means, for example57 * a hot-key to adjust backlight, the driver must notify the backlight58 * core that brightness has changed using backlight_force_update().59 *60 * The backlight driver core receives notifications from fbdev and61 * if the event is FB_EVENT_BLANK and if the value of blank, from the62 * FBIOBLANK ioctrl, results in a change in the backlight state the63 * update_status() operation is called.64 */65 66static struct list_head backlight_dev_list;67static struct mutex backlight_dev_list_mutex;68static struct blocking_notifier_head backlight_notifier;69 70static const char *const backlight_types[] = {71 [BACKLIGHT_RAW] = "raw",72 [BACKLIGHT_PLATFORM] = "platform",73 [BACKLIGHT_FIRMWARE] = "firmware",74};75 76static 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 82#if defined(CONFIG_FB_CORE) || (defined(CONFIG_FB_CORE_MODULE) && \83 defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))84/*85 * fb_notifier_callback86 *87 * This callback gets called when something important happens inside a88 * framebuffer driver. The backlight core only cares about FB_BLANK_UNBLANK89 * 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 reported94 * if there is a change in backlight for the specified fbdev.95 */96static 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;101 struct fb_info *info = evdata->info;102 struct backlight_device *fb_bd = fb_bl_device(info);103 int node = info->node;104 int fb_blank = 0;105 106 /* If we aren't interested in this event, skip it immediately ... */107 if (event != FB_EVENT_BLANK)108 return 0;109 110 bd = container_of(self, struct backlight_device, fb_notif);111 mutex_lock(&bd->ops_lock);112 113 if (!bd->ops)114 goto out;115 if (bd->ops->controls_device && !bd->ops->controls_device(bd, info->device))116 goto out;117 if (fb_bd && fb_bd != bd)118 goto out;119 120 fb_blank = *(int *)evdata->data;121 if (fb_blank == FB_BLANK_UNBLANK && !bd->fb_bl_on[node]) {122 bd->fb_bl_on[node] = true;123 if (!bd->use_count++) {124 bd->props.state &= ~BL_CORE_FBBLANK;125 backlight_update_status(bd);126 }127 } else if (fb_blank != FB_BLANK_UNBLANK && bd->fb_bl_on[node]) {128 bd->fb_bl_on[node] = false;129 if (!(--bd->use_count)) {130 bd->props.state |= BL_CORE_FBBLANK;131 backlight_update_status(bd);132 }133 }134out:135 mutex_unlock(&bd->ops_lock);136 return 0;137}138 139static int backlight_register_fb(struct backlight_device *bd)140{141 memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));142 bd->fb_notif.notifier_call = fb_notifier_callback;143 144 return fb_register_client(&bd->fb_notif);145}146 147static void backlight_unregister_fb(struct backlight_device *bd)148{149 fb_unregister_client(&bd->fb_notif);150}151#else152static inline int backlight_register_fb(struct backlight_device *bd)153{154 return 0;155}156 157static inline void backlight_unregister_fb(struct backlight_device *bd)158{159}160#endif /* CONFIG_FB_CORE */161 162static void backlight_generate_event(struct backlight_device *bd,163 enum backlight_update_reason reason)164{165 char *envp[2];166 167 switch (reason) {168 case BACKLIGHT_UPDATE_SYSFS:169 envp[0] = "SOURCE=sysfs";170 break;171 case BACKLIGHT_UPDATE_HOTKEY:172 envp[0] = "SOURCE=hotkey";173 break;174 default:175 envp[0] = "SOURCE=unknown";176 break;177 }178 envp[1] = NULL;179 kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);180 sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");181}182 183static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr,184 char *buf)185{186 struct backlight_device *bd = to_backlight_device(dev);187 188 return sprintf(buf, "%d\n", bd->props.power);189}190 191static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr,192 const char *buf, size_t count)193{194 int rc;195 struct backlight_device *bd = to_backlight_device(dev);196 unsigned long power, old_power;197 198 rc = kstrtoul(buf, 0, &power);199 if (rc)200 return rc;201 202 rc = -ENXIO;203 mutex_lock(&bd->ops_lock);204 if (bd->ops) {205 pr_debug("set power to %lu\n", power);206 if (bd->props.power != power) {207 old_power = bd->props.power;208 bd->props.power = power;209 rc = backlight_update_status(bd);210 if (rc)211 bd->props.power = old_power;212 else213 rc = count;214 } else {215 rc = count;216 }217 }218 mutex_unlock(&bd->ops_lock);219 220 return rc;221}222static DEVICE_ATTR_RW(bl_power);223 224static ssize_t brightness_show(struct device *dev,225 struct device_attribute *attr, char *buf)226{227 struct backlight_device *bd = to_backlight_device(dev);228 229 return sprintf(buf, "%d\n", bd->props.brightness);230}231 232int backlight_device_set_brightness(struct backlight_device *bd,233 unsigned long brightness)234{235 int rc = -ENXIO;236 237 mutex_lock(&bd->ops_lock);238 if (bd->ops) {239 if (brightness > bd->props.max_brightness)240 rc = -EINVAL;241 else {242 pr_debug("set brightness to %lu\n", brightness);243 bd->props.brightness = brightness;244 rc = backlight_update_status(bd);245 }246 }247 mutex_unlock(&bd->ops_lock);248 249 backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);250 251 return rc;252}253EXPORT_SYMBOL(backlight_device_set_brightness);254 255static ssize_t brightness_store(struct device *dev,256 struct device_attribute *attr, const char *buf, size_t count)257{258 int rc;259 struct backlight_device *bd = to_backlight_device(dev);260 unsigned long brightness;261 262 rc = kstrtoul(buf, 0, &brightness);263 if (rc)264 return rc;265 266 rc = backlight_device_set_brightness(bd, brightness);267 268 return rc ? rc : count;269}270static DEVICE_ATTR_RW(brightness);271 272static ssize_t type_show(struct device *dev, struct device_attribute *attr,273 char *buf)274{275 struct backlight_device *bd = to_backlight_device(dev);276 277 return sprintf(buf, "%s\n", backlight_types[bd->props.type]);278}279static DEVICE_ATTR_RO(type);280 281static ssize_t max_brightness_show(struct device *dev,282 struct device_attribute *attr, char *buf)283{284 struct backlight_device *bd = to_backlight_device(dev);285 286 return sprintf(buf, "%d\n", bd->props.max_brightness);287}288static DEVICE_ATTR_RO(max_brightness);289 290static ssize_t actual_brightness_show(struct device *dev,291 struct device_attribute *attr, char *buf)292{293 int rc = -ENXIO;294 struct backlight_device *bd = to_backlight_device(dev);295 296 mutex_lock(&bd->ops_lock);297 if (bd->ops && bd->ops->get_brightness) {298 rc = bd->ops->get_brightness(bd);299 if (rc >= 0)300 rc = sprintf(buf, "%d\n", rc);301 } else {302 rc = sprintf(buf, "%d\n", bd->props.brightness);303 }304 mutex_unlock(&bd->ops_lock);305 306 return rc;307}308static DEVICE_ATTR_RO(actual_brightness);309 310static ssize_t scale_show(struct device *dev,311 struct device_attribute *attr, char *buf)312{313 struct backlight_device *bd = to_backlight_device(dev);314 315 if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR))316 return sprintf(buf, "unknown\n");317 318 return sprintf(buf, "%s\n", backlight_scale_types[bd->props.scale]);319}320static DEVICE_ATTR_RO(scale);321 322#ifdef CONFIG_PM_SLEEP323static int backlight_suspend(struct device *dev)324{325 struct backlight_device *bd = to_backlight_device(dev);326 327 mutex_lock(&bd->ops_lock);328 if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {329 bd->props.state |= BL_CORE_SUSPENDED;330 backlight_update_status(bd);331 }332 mutex_unlock(&bd->ops_lock);333 334 return 0;335}336 337static int backlight_resume(struct device *dev)338{339 struct backlight_device *bd = to_backlight_device(dev);340 341 mutex_lock(&bd->ops_lock);342 if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {343 bd->props.state &= ~BL_CORE_SUSPENDED;344 backlight_update_status(bd);345 }346 mutex_unlock(&bd->ops_lock);347 348 return 0;349}350#endif351 352static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend,353 backlight_resume);354 355static void bl_device_release(struct device *dev)356{357 struct backlight_device *bd = to_backlight_device(dev);358 kfree(bd);359}360 361static 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,366 &dev_attr_scale.attr,367 &dev_attr_type.attr,368 NULL,369};370ATTRIBUTE_GROUPS(bl_device);371 372static const struct class backlight_class = {373 .name = "backlight",374 .dev_groups = bl_device_groups,375 .pm = &backlight_class_dev_pm_ops,376};377 378/**379 * backlight_force_update - tell the backlight subsystem that hardware state380 * has changed381 * @bd: the backlight device to update382 * @reason: reason for update383 *384 * Updates the internal state of the backlight in response to a hardware event,385 * and generates an uevent to notify userspace. A backlight driver shall call386 * backlight_force_update() when the backlight is changed using, for example,387 * a hot-key. The updated brightness is read using get_brightness() and the388 * brightness value is reported using an uevent.389 */390void backlight_force_update(struct backlight_device *bd,391 enum backlight_update_reason reason)392{393 int brightness;394 395 mutex_lock(&bd->ops_lock);396 if (bd->ops && bd->ops->get_brightness) {397 brightness = bd->ops->get_brightness(bd);398 if (brightness >= 0)399 bd->props.brightness = brightness;400 else401 dev_err(&bd->dev,402 "Could not update brightness from device: %pe\n",403 ERR_PTR(brightness));404 }405 mutex_unlock(&bd->ops_lock);406 backlight_generate_event(bd, reason);407}408EXPORT_SYMBOL(backlight_force_update);409 410/* deprecated - use devm_backlight_device_register() */411struct backlight_device *backlight_device_register(const char *name,412 struct device *parent, void *devdata, const struct backlight_ops *ops,413 const struct backlight_properties *props)414{415 struct backlight_device *new_bd;416 int rc;417 418 pr_debug("backlight_device_register: name=%s\n", name);419 420 new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);421 if (!new_bd)422 return ERR_PTR(-ENOMEM);423 424 mutex_init(&new_bd->update_lock);425 mutex_init(&new_bd->ops_lock);426 427 new_bd->dev.class = &backlight_class;428 new_bd->dev.parent = parent;429 new_bd->dev.release = bl_device_release;430 dev_set_name(&new_bd->dev, "%s", name);431 dev_set_drvdata(&new_bd->dev, devdata);432 433 /* Set default properties */434 if (props) {435 memcpy(&new_bd->props, props,436 sizeof(struct backlight_properties));437 if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) {438 WARN(1, "%s: invalid backlight type", name);439 new_bd->props.type = BACKLIGHT_RAW;440 }441 } else {442 new_bd->props.type = BACKLIGHT_RAW;443 }444 445 rc = device_register(&new_bd->dev);446 if (rc) {447 put_device(&new_bd->dev);448 return ERR_PTR(rc);449 }450 451 rc = backlight_register_fb(new_bd);452 if (rc) {453 device_unregister(&new_bd->dev);454 return ERR_PTR(rc);455 }456 457 new_bd->ops = ops;458 459#ifdef CONFIG_PMAC_BACKLIGHT460 mutex_lock(&pmac_backlight_mutex);461 if (!pmac_backlight)462 pmac_backlight = new_bd;463 mutex_unlock(&pmac_backlight_mutex);464#endif465 466 mutex_lock(&backlight_dev_list_mutex);467 list_add(&new_bd->entry, &backlight_dev_list);468 mutex_unlock(&backlight_dev_list_mutex);469 470 blocking_notifier_call_chain(&backlight_notifier,471 BACKLIGHT_REGISTERED, new_bd);472 473 return new_bd;474}475EXPORT_SYMBOL(backlight_device_register);476 477/** backlight_device_get_by_type - find first backlight device of a type478 * @type: the type of backlight device479 *480 * Look up the first backlight device of the specified type481 *482 * RETURNS:483 *484 * Pointer to backlight device if any was found. Otherwise NULL.485 */486struct backlight_device *backlight_device_get_by_type(enum backlight_type type)487{488 bool found = false;489 struct backlight_device *bd;490 491 mutex_lock(&backlight_dev_list_mutex);492 list_for_each_entry(bd, &backlight_dev_list, entry) {493 if (bd->props.type == type) {494 found = true;495 break;496 }497 }498 mutex_unlock(&backlight_dev_list_mutex);499 500 return found ? bd : NULL;501}502EXPORT_SYMBOL(backlight_device_get_by_type);503 504/**505 * backlight_device_get_by_name - Get backlight device by name506 * @name: Device name507 *508 * This function looks up a backlight device by its name. It obtains a reference509 * on the backlight device and it is the caller's responsibility to drop the510 * reference by calling put_device().511 *512 * Returns:513 * A pointer to the backlight device if found, otherwise NULL.514 */515struct backlight_device *backlight_device_get_by_name(const char *name)516{517 struct device *dev;518 519 dev = class_find_device_by_name(&backlight_class, name);520 521 return dev ? to_backlight_device(dev) : NULL;522}523EXPORT_SYMBOL(backlight_device_get_by_name);524 525/* deprecated - use devm_backlight_device_unregister() */526void backlight_device_unregister(struct backlight_device *bd)527{528 if (!bd)529 return;530 531 mutex_lock(&backlight_dev_list_mutex);532 list_del(&bd->entry);533 mutex_unlock(&backlight_dev_list_mutex);534 535#ifdef CONFIG_PMAC_BACKLIGHT536 mutex_lock(&pmac_backlight_mutex);537 if (pmac_backlight == bd)538 pmac_backlight = NULL;539 mutex_unlock(&pmac_backlight_mutex);540#endif541 542 blocking_notifier_call_chain(&backlight_notifier,543 BACKLIGHT_UNREGISTERED, bd);544 545 mutex_lock(&bd->ops_lock);546 bd->ops = NULL;547 mutex_unlock(&bd->ops_lock);548 549 backlight_unregister_fb(bd);550 device_unregister(&bd->dev);551}552EXPORT_SYMBOL(backlight_device_unregister);553 554static void devm_backlight_device_release(struct device *dev, void *res)555{556 struct backlight_device *backlight = *(struct backlight_device **)res;557 558 backlight_device_unregister(backlight);559}560 561static int devm_backlight_device_match(struct device *dev, void *res,562 void *data)563{564 struct backlight_device **r = res;565 566 return *r == data;567}568 569/**570 * backlight_register_notifier - get notified of backlight (un)registration571 * @nb: notifier block with the notifier to call on backlight (un)registration572 *573 * Register a notifier to get notified when backlight devices get registered574 * or unregistered.575 *576 * RETURNS:577 *578 * 0 on success, otherwise a negative error code579 */580int backlight_register_notifier(struct notifier_block *nb)581{582 return blocking_notifier_chain_register(&backlight_notifier, nb);583}584EXPORT_SYMBOL(backlight_register_notifier);585 586/**587 * backlight_unregister_notifier - unregister a backlight notifier588 * @nb: notifier block to unregister589 *590 * Register a notifier to get notified when backlight devices get registered591 * or unregistered.592 *593 * RETURNS:594 *595 * 0 on success, otherwise a negative error code596 */597int backlight_unregister_notifier(struct notifier_block *nb)598{599 return blocking_notifier_chain_unregister(&backlight_notifier, nb);600}601EXPORT_SYMBOL(backlight_unregister_notifier);602 603/**604 * devm_backlight_device_register - register a new backlight device605 * @dev: the device to register606 * @name: the name of the device607 * @parent: a pointer to the parent device (often the same as @dev)608 * @devdata: an optional pointer to be stored for private driver use609 * @ops: the backlight operations structure610 * @props: the backlight properties611 *612 * Creates and registers new backlight device. When a backlight device613 * is registered the configuration must be specified in the @props614 * parameter. See description of &backlight_properties.615 *616 * RETURNS:617 *618 * struct backlight on success, or an ERR_PTR on error619 */620struct backlight_device *devm_backlight_device_register(struct device *dev,621 const char *name, struct device *parent, void *devdata,622 const struct backlight_ops *ops,623 const struct backlight_properties *props)624{625 struct backlight_device **ptr, *backlight;626 627 ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr),628 GFP_KERNEL);629 if (!ptr)630 return ERR_PTR(-ENOMEM);631 632 backlight = backlight_device_register(name, parent, devdata, ops,633 props);634 if (!IS_ERR(backlight)) {635 *ptr = backlight;636 devres_add(dev, ptr);637 } else {638 devres_free(ptr);639 }640 641 return backlight;642}643EXPORT_SYMBOL(devm_backlight_device_register);644 645/**646 * devm_backlight_device_unregister - unregister backlight device647 * @dev: the device to unregister648 * @bd: the backlight device to unregister649 *650 * Deallocates a backlight allocated with devm_backlight_device_register().651 * Normally this function will not need to be called and the resource management652 * code will ensure that the resources are freed.653 */654void devm_backlight_device_unregister(struct device *dev,655 struct backlight_device *bd)656{657 int rc;658 659 rc = devres_release(dev, devm_backlight_device_release,660 devm_backlight_device_match, bd);661 WARN_ON(rc);662}663EXPORT_SYMBOL(devm_backlight_device_unregister);664 665#ifdef CONFIG_OF666static int of_parent_match(struct device *dev, const void *data)667{668 return dev->parent && dev->parent->of_node == data;669}670 671/**672 * of_find_backlight_by_node() - find backlight device by device-tree node673 * @node: device-tree node of the backlight device674 *675 * Returns a pointer to the backlight device corresponding to the given DT676 * node or NULL if no such backlight device exists or if the device hasn't677 * been probed yet.678 *679 * This function obtains a reference on the backlight device and it is the680 * caller's responsibility to drop the reference by calling put_device() on681 * the backlight device's .dev field.682 */683struct backlight_device *of_find_backlight_by_node(struct device_node *node)684{685 struct device *dev;686 687 dev = class_find_device(&backlight_class, NULL, node, of_parent_match);688 689 return dev ? to_backlight_device(dev) : NULL;690}691EXPORT_SYMBOL(of_find_backlight_by_node);692#endif693 694static struct backlight_device *of_find_backlight(struct device *dev)695{696 struct backlight_device *bd = NULL;697 struct device_node *np;698 699 if (!dev)700 return NULL;701 702 if (IS_ENABLED(CONFIG_OF) && dev->of_node) {703 np = of_parse_phandle(dev->of_node, "backlight", 0);704 if (np) {705 bd = of_find_backlight_by_node(np);706 of_node_put(np);707 if (!bd)708 return ERR_PTR(-EPROBE_DEFER);709 }710 }711 712 return bd;713}714 715static void devm_backlight_release(void *data)716{717 struct backlight_device *bd = data;718 719 put_device(&bd->dev);720}721 722/**723 * devm_of_find_backlight - find backlight for a device724 * @dev: the device725 *726 * This function looks for a property named 'backlight' on the DT node727 * connected to @dev and looks up the backlight device. The lookup is728 * device managed so the reference to the backlight device is automatically729 * dropped on driver detach.730 *731 * RETURNS:732 *733 * A pointer to the backlight device if found.734 * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight735 * device is found. NULL if there's no backlight property.736 */737struct backlight_device *devm_of_find_backlight(struct device *dev)738{739 struct backlight_device *bd;740 int ret;741 742 bd = of_find_backlight(dev);743 if (IS_ERR_OR_NULL(bd))744 return bd;745 ret = devm_add_action_or_reset(dev, devm_backlight_release, bd);746 if (ret)747 return ERR_PTR(ret);748 749 return bd;750}751EXPORT_SYMBOL(devm_of_find_backlight);752 753static void __exit backlight_class_exit(void)754{755 class_unregister(&backlight_class);756}757 758static int __init backlight_class_init(void)759{760 int ret;761 762 ret = class_register(&backlight_class);763 if (ret) {764 pr_warn("Unable to create backlight class; errno = %d\n", ret);765 return ret;766 }767 768 INIT_LIST_HEAD(&backlight_dev_list);769 mutex_init(&backlight_dev_list_mutex);770 BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);771 772 return 0;773}774 775/*776 * if this is compiled into the kernel, we need to ensure that the777 * class is registered before users of the class try to register lcd's778 */779postcore_initcall(backlight_class_init);780module_exit(backlight_class_exit);781 782MODULE_LICENSE("GPL");783MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");784MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");785