162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Backlight Lowlevel Control Abstraction 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003,2004 Hewlett-Packard Company 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/backlight.h> 1562306a36Sopenharmony_ci#include <linux/notifier.h> 1662306a36Sopenharmony_ci#include <linux/ctype.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/fb.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 2262306a36Sopenharmony_ci#include <asm/backlight.h> 2362306a36Sopenharmony_ci#endif 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/** 2662306a36Sopenharmony_ci * DOC: overview 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * The backlight core supports implementing backlight drivers. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * A backlight driver registers a driver using 3162306a36Sopenharmony_ci * devm_backlight_device_register(). The properties of the backlight 3262306a36Sopenharmony_ci * driver such as type and max_brightness must be specified. 3362306a36Sopenharmony_ci * When the core detect changes in for example brightness or power state 3462306a36Sopenharmony_ci * the update_status() operation is called. The backlight driver shall 3562306a36Sopenharmony_ci * implement this operation and use it to adjust backlight. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Several sysfs attributes are provided by the backlight core:: 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * - brightness R/W, set the requested brightness level 4062306a36Sopenharmony_ci * - actual_brightness RO, the brightness level used by the HW 4162306a36Sopenharmony_ci * - max_brightness RO, the maximum brightness level supported 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * See Documentation/ABI/stable/sysfs-class-backlight for the full list. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * The backlight can be adjusted using the sysfs interface, and 4662306a36Sopenharmony_ci * the backlight driver may also support adjusting backlight using 4762306a36Sopenharmony_ci * a hot-key or some other platform or firmware specific way. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * The driver must implement the get_brightness() operation if 5062306a36Sopenharmony_ci * the HW do not support all the levels that can be specified in 5162306a36Sopenharmony_ci * brightness, thus providing user-space access to the actual level 5262306a36Sopenharmony_ci * via the actual_brightness attribute. 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * When the backlight changes this is reported to user-space using 5562306a36Sopenharmony_ci * an uevent connected to the actual_brightness attribute. 5662306a36Sopenharmony_ci * When brightness is set by platform specific means, for example 5762306a36Sopenharmony_ci * a hot-key to adjust backlight, the driver must notify the backlight 5862306a36Sopenharmony_ci * core that brightness has changed using backlight_force_update(). 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * The backlight driver core receives notifications from fbdev and 6162306a36Sopenharmony_ci * if the event is FB_EVENT_BLANK and if the value of blank, from the 6262306a36Sopenharmony_ci * FBIOBLANK ioctrl, results in a change in the backlight state the 6362306a36Sopenharmony_ci * update_status() operation is called. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic struct list_head backlight_dev_list; 6762306a36Sopenharmony_cistatic struct mutex backlight_dev_list_mutex; 6862306a36Sopenharmony_cistatic struct blocking_notifier_head backlight_notifier; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const char *const backlight_types[] = { 7162306a36Sopenharmony_ci [BACKLIGHT_RAW] = "raw", 7262306a36Sopenharmony_ci [BACKLIGHT_PLATFORM] = "platform", 7362306a36Sopenharmony_ci [BACKLIGHT_FIRMWARE] = "firmware", 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic const char *const backlight_scale_types[] = { 7762306a36Sopenharmony_ci [BACKLIGHT_SCALE_UNKNOWN] = "unknown", 7862306a36Sopenharmony_ci [BACKLIGHT_SCALE_LINEAR] = "linear", 7962306a36Sopenharmony_ci [BACKLIGHT_SCALE_NON_LINEAR] = "non-linear", 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#if defined(CONFIG_FB_CORE) || (defined(CONFIG_FB_CORE_MODULE) && \ 8362306a36Sopenharmony_ci defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)) 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * fb_notifier_callback 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * This callback gets called when something important happens inside a 8862306a36Sopenharmony_ci * framebuffer driver. The backlight core only cares about FB_BLANK_UNBLANK 8962306a36Sopenharmony_ci * which is reported to the driver using backlight_update_status() 9062306a36Sopenharmony_ci * as a state change. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * There may be several fbdev's connected to the backlight device, 9362306a36Sopenharmony_ci * in which case they are kept track of. A state change is only reported 9462306a36Sopenharmony_ci * if there is a change in backlight for the specified fbdev. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic int fb_notifier_callback(struct notifier_block *self, 9762306a36Sopenharmony_ci unsigned long event, void *data) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct backlight_device *bd; 10062306a36Sopenharmony_ci struct fb_event *evdata = data; 10162306a36Sopenharmony_ci int node = evdata->info->node; 10262306a36Sopenharmony_ci int fb_blank = 0; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* If we aren't interested in this event, skip it immediately ... */ 10562306a36Sopenharmony_ci if (event != FB_EVENT_BLANK) 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci bd = container_of(self, struct backlight_device, fb_notif); 10962306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!bd->ops) 11262306a36Sopenharmony_ci goto out; 11362306a36Sopenharmony_ci if (bd->ops->check_fb && !bd->ops->check_fb(bd, evdata->info)) 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci fb_blank = *(int *)evdata->data; 11762306a36Sopenharmony_ci if (fb_blank == FB_BLANK_UNBLANK && !bd->fb_bl_on[node]) { 11862306a36Sopenharmony_ci bd->fb_bl_on[node] = true; 11962306a36Sopenharmony_ci if (!bd->use_count++) { 12062306a36Sopenharmony_ci bd->props.state &= ~BL_CORE_FBBLANK; 12162306a36Sopenharmony_ci bd->props.fb_blank = FB_BLANK_UNBLANK; 12262306a36Sopenharmony_ci backlight_update_status(bd); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } else if (fb_blank != FB_BLANK_UNBLANK && bd->fb_bl_on[node]) { 12562306a36Sopenharmony_ci bd->fb_bl_on[node] = false; 12662306a36Sopenharmony_ci if (!(--bd->use_count)) { 12762306a36Sopenharmony_ci bd->props.state |= BL_CORE_FBBLANK; 12862306a36Sopenharmony_ci bd->props.fb_blank = fb_blank; 12962306a36Sopenharmony_ci backlight_update_status(bd); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ciout: 13362306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int backlight_register_fb(struct backlight_device *bd) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci memset(&bd->fb_notif, 0, sizeof(bd->fb_notif)); 14062306a36Sopenharmony_ci bd->fb_notif.notifier_call = fb_notifier_callback; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return fb_register_client(&bd->fb_notif); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void backlight_unregister_fb(struct backlight_device *bd) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci fb_unregister_client(&bd->fb_notif); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci#else 15062306a36Sopenharmony_cistatic inline int backlight_register_fb(struct backlight_device *bd) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic inline void backlight_unregister_fb(struct backlight_device *bd) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci#endif /* CONFIG_FB_CORE */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void backlight_generate_event(struct backlight_device *bd, 16162306a36Sopenharmony_ci enum backlight_update_reason reason) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci char *envp[2]; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci switch (reason) { 16662306a36Sopenharmony_ci case BACKLIGHT_UPDATE_SYSFS: 16762306a36Sopenharmony_ci envp[0] = "SOURCE=sysfs"; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case BACKLIGHT_UPDATE_HOTKEY: 17062306a36Sopenharmony_ci envp[0] = "SOURCE=hotkey"; 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci default: 17362306a36Sopenharmony_ci envp[0] = "SOURCE=unknown"; 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci envp[1] = NULL; 17762306a36Sopenharmony_ci kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp); 17862306a36Sopenharmony_ci sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness"); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic ssize_t bl_power_show(struct device *dev, struct device_attribute *attr, 18262306a36Sopenharmony_ci char *buf) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return sprintf(buf, "%d\n", bd->props.power); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic ssize_t bl_power_store(struct device *dev, struct device_attribute *attr, 19062306a36Sopenharmony_ci const char *buf, size_t count) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int rc; 19362306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 19462306a36Sopenharmony_ci unsigned long power, old_power; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci rc = kstrtoul(buf, 0, &power); 19762306a36Sopenharmony_ci if (rc) 19862306a36Sopenharmony_ci return rc; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci rc = -ENXIO; 20162306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 20262306a36Sopenharmony_ci if (bd->ops) { 20362306a36Sopenharmony_ci pr_debug("set power to %lu\n", power); 20462306a36Sopenharmony_ci if (bd->props.power != power) { 20562306a36Sopenharmony_ci old_power = bd->props.power; 20662306a36Sopenharmony_ci bd->props.power = power; 20762306a36Sopenharmony_ci rc = backlight_update_status(bd); 20862306a36Sopenharmony_ci if (rc) 20962306a36Sopenharmony_ci bd->props.power = old_power; 21062306a36Sopenharmony_ci else 21162306a36Sopenharmony_ci rc = count; 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci rc = count; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return rc; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(bl_power); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic ssize_t brightness_show(struct device *dev, 22362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return sprintf(buf, "%d\n", bd->props.brightness); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciint backlight_device_set_brightness(struct backlight_device *bd, 23162306a36Sopenharmony_ci unsigned long brightness) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci int rc = -ENXIO; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 23662306a36Sopenharmony_ci if (bd->ops) { 23762306a36Sopenharmony_ci if (brightness > bd->props.max_brightness) 23862306a36Sopenharmony_ci rc = -EINVAL; 23962306a36Sopenharmony_ci else { 24062306a36Sopenharmony_ci pr_debug("set brightness to %lu\n", brightness); 24162306a36Sopenharmony_ci bd->props.brightness = brightness; 24262306a36Sopenharmony_ci rc = backlight_update_status(bd); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return rc; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_device_set_brightness); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic ssize_t brightness_store(struct device *dev, 25462306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int rc; 25762306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 25862306a36Sopenharmony_ci unsigned long brightness; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci rc = kstrtoul(buf, 0, &brightness); 26162306a36Sopenharmony_ci if (rc) 26262306a36Sopenharmony_ci return rc; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci rc = backlight_device_set_brightness(bd, brightness); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return rc ? rc : count; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(brightness); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic ssize_t type_show(struct device *dev, struct device_attribute *attr, 27162306a36Sopenharmony_ci char *buf) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return sprintf(buf, "%s\n", backlight_types[bd->props.type]); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(type); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic ssize_t max_brightness_show(struct device *dev, 28062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return sprintf(buf, "%d\n", bd->props.max_brightness); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(max_brightness); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic ssize_t actual_brightness_show(struct device *dev, 28962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int rc = -ENXIO; 29262306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 29562306a36Sopenharmony_ci if (bd->ops && bd->ops->get_brightness) { 29662306a36Sopenharmony_ci rc = bd->ops->get_brightness(bd); 29762306a36Sopenharmony_ci if (rc >= 0) 29862306a36Sopenharmony_ci rc = sprintf(buf, "%d\n", rc); 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci rc = sprintf(buf, "%d\n", bd->props.brightness); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return rc; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(actual_brightness); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic ssize_t scale_show(struct device *dev, 30962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR)) 31462306a36Sopenharmony_ci return sprintf(buf, "unknown\n"); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return sprintf(buf, "%s\n", backlight_scale_types[bd->props.scale]); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(scale); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic struct class *backlight_class; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 32362306a36Sopenharmony_cistatic int backlight_suspend(struct device *dev) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 32862306a36Sopenharmony_ci if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { 32962306a36Sopenharmony_ci bd->props.state |= BL_CORE_SUSPENDED; 33062306a36Sopenharmony_ci backlight_update_status(bd); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int backlight_resume(struct device *dev) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 34262306a36Sopenharmony_ci if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) { 34362306a36Sopenharmony_ci bd->props.state &= ~BL_CORE_SUSPENDED; 34462306a36Sopenharmony_ci backlight_update_status(bd); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci#endif 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend, 35362306a36Sopenharmony_ci backlight_resume); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void bl_device_release(struct device *dev) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct backlight_device *bd = to_backlight_device(dev); 35862306a36Sopenharmony_ci kfree(bd); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic struct attribute *bl_device_attrs[] = { 36262306a36Sopenharmony_ci &dev_attr_bl_power.attr, 36362306a36Sopenharmony_ci &dev_attr_brightness.attr, 36462306a36Sopenharmony_ci &dev_attr_actual_brightness.attr, 36562306a36Sopenharmony_ci &dev_attr_max_brightness.attr, 36662306a36Sopenharmony_ci &dev_attr_scale.attr, 36762306a36Sopenharmony_ci &dev_attr_type.attr, 36862306a36Sopenharmony_ci NULL, 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ciATTRIBUTE_GROUPS(bl_device); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/** 37362306a36Sopenharmony_ci * backlight_force_update - tell the backlight subsystem that hardware state 37462306a36Sopenharmony_ci * has changed 37562306a36Sopenharmony_ci * @bd: the backlight device to update 37662306a36Sopenharmony_ci * @reason: reason for update 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * Updates the internal state of the backlight in response to a hardware event, 37962306a36Sopenharmony_ci * and generates an uevent to notify userspace. A backlight driver shall call 38062306a36Sopenharmony_ci * backlight_force_update() when the backlight is changed using, for example, 38162306a36Sopenharmony_ci * a hot-key. The updated brightness is read using get_brightness() and the 38262306a36Sopenharmony_ci * brightness value is reported using an uevent. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_civoid backlight_force_update(struct backlight_device *bd, 38562306a36Sopenharmony_ci enum backlight_update_reason reason) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci int brightness; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 39062306a36Sopenharmony_ci if (bd->ops && bd->ops->get_brightness) { 39162306a36Sopenharmony_ci brightness = bd->ops->get_brightness(bd); 39262306a36Sopenharmony_ci if (brightness >= 0) 39362306a36Sopenharmony_ci bd->props.brightness = brightness; 39462306a36Sopenharmony_ci else 39562306a36Sopenharmony_ci dev_err(&bd->dev, 39662306a36Sopenharmony_ci "Could not update brightness from device: %pe\n", 39762306a36Sopenharmony_ci ERR_PTR(brightness)); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 40062306a36Sopenharmony_ci backlight_generate_event(bd, reason); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_force_update); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/* deprecated - use devm_backlight_device_register() */ 40562306a36Sopenharmony_cistruct backlight_device *backlight_device_register(const char *name, 40662306a36Sopenharmony_ci struct device *parent, void *devdata, const struct backlight_ops *ops, 40762306a36Sopenharmony_ci const struct backlight_properties *props) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct backlight_device *new_bd; 41062306a36Sopenharmony_ci int rc; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci pr_debug("backlight_device_register: name=%s\n", name); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); 41562306a36Sopenharmony_ci if (!new_bd) 41662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci mutex_init(&new_bd->update_lock); 41962306a36Sopenharmony_ci mutex_init(&new_bd->ops_lock); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci new_bd->dev.class = backlight_class; 42262306a36Sopenharmony_ci new_bd->dev.parent = parent; 42362306a36Sopenharmony_ci new_bd->dev.release = bl_device_release; 42462306a36Sopenharmony_ci dev_set_name(&new_bd->dev, "%s", name); 42562306a36Sopenharmony_ci dev_set_drvdata(&new_bd->dev, devdata); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Set default properties */ 42862306a36Sopenharmony_ci if (props) { 42962306a36Sopenharmony_ci memcpy(&new_bd->props, props, 43062306a36Sopenharmony_ci sizeof(struct backlight_properties)); 43162306a36Sopenharmony_ci if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) { 43262306a36Sopenharmony_ci WARN(1, "%s: invalid backlight type", name); 43362306a36Sopenharmony_ci new_bd->props.type = BACKLIGHT_RAW; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } else { 43662306a36Sopenharmony_ci new_bd->props.type = BACKLIGHT_RAW; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci rc = device_register(&new_bd->dev); 44062306a36Sopenharmony_ci if (rc) { 44162306a36Sopenharmony_ci put_device(&new_bd->dev); 44262306a36Sopenharmony_ci return ERR_PTR(rc); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci rc = backlight_register_fb(new_bd); 44662306a36Sopenharmony_ci if (rc) { 44762306a36Sopenharmony_ci device_unregister(&new_bd->dev); 44862306a36Sopenharmony_ci return ERR_PTR(rc); 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci new_bd->ops = ops; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 45462306a36Sopenharmony_ci mutex_lock(&pmac_backlight_mutex); 45562306a36Sopenharmony_ci if (!pmac_backlight) 45662306a36Sopenharmony_ci pmac_backlight = new_bd; 45762306a36Sopenharmony_ci mutex_unlock(&pmac_backlight_mutex); 45862306a36Sopenharmony_ci#endif 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci mutex_lock(&backlight_dev_list_mutex); 46162306a36Sopenharmony_ci list_add(&new_bd->entry, &backlight_dev_list); 46262306a36Sopenharmony_ci mutex_unlock(&backlight_dev_list_mutex); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci blocking_notifier_call_chain(&backlight_notifier, 46562306a36Sopenharmony_ci BACKLIGHT_REGISTERED, new_bd); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return new_bd; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_device_register); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/** backlight_device_get_by_type - find first backlight device of a type 47262306a36Sopenharmony_ci * @type: the type of backlight device 47362306a36Sopenharmony_ci * 47462306a36Sopenharmony_ci * Look up the first backlight device of the specified type 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * RETURNS: 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * Pointer to backlight device if any was found. Otherwise NULL. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_cistruct backlight_device *backlight_device_get_by_type(enum backlight_type type) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci bool found = false; 48362306a36Sopenharmony_ci struct backlight_device *bd; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci mutex_lock(&backlight_dev_list_mutex); 48662306a36Sopenharmony_ci list_for_each_entry(bd, &backlight_dev_list, entry) { 48762306a36Sopenharmony_ci if (bd->props.type == type) { 48862306a36Sopenharmony_ci found = true; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci mutex_unlock(&backlight_dev_list_mutex); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return found ? bd : NULL; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_device_get_by_type); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/** 49962306a36Sopenharmony_ci * backlight_device_get_by_name - Get backlight device by name 50062306a36Sopenharmony_ci * @name: Device name 50162306a36Sopenharmony_ci * 50262306a36Sopenharmony_ci * This function looks up a backlight device by its name. It obtains a reference 50362306a36Sopenharmony_ci * on the backlight device and it is the caller's responsibility to drop the 50462306a36Sopenharmony_ci * reference by calling put_device(). 50562306a36Sopenharmony_ci * 50662306a36Sopenharmony_ci * Returns: 50762306a36Sopenharmony_ci * A pointer to the backlight device if found, otherwise NULL. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_cistruct backlight_device *backlight_device_get_by_name(const char *name) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct device *dev; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci dev = class_find_device_by_name(backlight_class, name); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return dev ? to_backlight_device(dev) : NULL; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_device_get_by_name); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* deprecated - use devm_backlight_device_unregister() */ 52062306a36Sopenharmony_civoid backlight_device_unregister(struct backlight_device *bd) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci if (!bd) 52362306a36Sopenharmony_ci return; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci mutex_lock(&backlight_dev_list_mutex); 52662306a36Sopenharmony_ci list_del(&bd->entry); 52762306a36Sopenharmony_ci mutex_unlock(&backlight_dev_list_mutex); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 53062306a36Sopenharmony_ci mutex_lock(&pmac_backlight_mutex); 53162306a36Sopenharmony_ci if (pmac_backlight == bd) 53262306a36Sopenharmony_ci pmac_backlight = NULL; 53362306a36Sopenharmony_ci mutex_unlock(&pmac_backlight_mutex); 53462306a36Sopenharmony_ci#endif 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci blocking_notifier_call_chain(&backlight_notifier, 53762306a36Sopenharmony_ci BACKLIGHT_UNREGISTERED, bd); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci mutex_lock(&bd->ops_lock); 54062306a36Sopenharmony_ci bd->ops = NULL; 54162306a36Sopenharmony_ci mutex_unlock(&bd->ops_lock); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci backlight_unregister_fb(bd); 54462306a36Sopenharmony_ci device_unregister(&bd->dev); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_device_unregister); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void devm_backlight_device_release(struct device *dev, void *res) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct backlight_device *backlight = *(struct backlight_device **)res; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci backlight_device_unregister(backlight); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int devm_backlight_device_match(struct device *dev, void *res, 55662306a36Sopenharmony_ci void *data) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct backlight_device **r = res; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return *r == data; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/** 56462306a36Sopenharmony_ci * backlight_register_notifier - get notified of backlight (un)registration 56562306a36Sopenharmony_ci * @nb: notifier block with the notifier to call on backlight (un)registration 56662306a36Sopenharmony_ci * 56762306a36Sopenharmony_ci * Register a notifier to get notified when backlight devices get registered 56862306a36Sopenharmony_ci * or unregistered. 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * RETURNS: 57162306a36Sopenharmony_ci * 57262306a36Sopenharmony_ci * 0 on success, otherwise a negative error code 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ciint backlight_register_notifier(struct notifier_block *nb) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci return blocking_notifier_chain_register(&backlight_notifier, nb); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_register_notifier); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/** 58162306a36Sopenharmony_ci * backlight_unregister_notifier - unregister a backlight notifier 58262306a36Sopenharmony_ci * @nb: notifier block to unregister 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * Register a notifier to get notified when backlight devices get registered 58562306a36Sopenharmony_ci * or unregistered. 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * RETURNS: 58862306a36Sopenharmony_ci * 58962306a36Sopenharmony_ci * 0 on success, otherwise a negative error code 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ciint backlight_unregister_notifier(struct notifier_block *nb) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci return blocking_notifier_chain_unregister(&backlight_notifier, nb); 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ciEXPORT_SYMBOL(backlight_unregister_notifier); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/** 59862306a36Sopenharmony_ci * devm_backlight_device_register - register a new backlight device 59962306a36Sopenharmony_ci * @dev: the device to register 60062306a36Sopenharmony_ci * @name: the name of the device 60162306a36Sopenharmony_ci * @parent: a pointer to the parent device (often the same as @dev) 60262306a36Sopenharmony_ci * @devdata: an optional pointer to be stored for private driver use 60362306a36Sopenharmony_ci * @ops: the backlight operations structure 60462306a36Sopenharmony_ci * @props: the backlight properties 60562306a36Sopenharmony_ci * 60662306a36Sopenharmony_ci * Creates and registers new backlight device. When a backlight device 60762306a36Sopenharmony_ci * is registered the configuration must be specified in the @props 60862306a36Sopenharmony_ci * parameter. See description of &backlight_properties. 60962306a36Sopenharmony_ci * 61062306a36Sopenharmony_ci * RETURNS: 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * struct backlight on success, or an ERR_PTR on error 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_cistruct backlight_device *devm_backlight_device_register(struct device *dev, 61562306a36Sopenharmony_ci const char *name, struct device *parent, void *devdata, 61662306a36Sopenharmony_ci const struct backlight_ops *ops, 61762306a36Sopenharmony_ci const struct backlight_properties *props) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct backlight_device **ptr, *backlight; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr), 62262306a36Sopenharmony_ci GFP_KERNEL); 62362306a36Sopenharmony_ci if (!ptr) 62462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci backlight = backlight_device_register(name, parent, devdata, ops, 62762306a36Sopenharmony_ci props); 62862306a36Sopenharmony_ci if (!IS_ERR(backlight)) { 62962306a36Sopenharmony_ci *ptr = backlight; 63062306a36Sopenharmony_ci devres_add(dev, ptr); 63162306a36Sopenharmony_ci } else { 63262306a36Sopenharmony_ci devres_free(ptr); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return backlight; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ciEXPORT_SYMBOL(devm_backlight_device_register); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/** 64062306a36Sopenharmony_ci * devm_backlight_device_unregister - unregister backlight device 64162306a36Sopenharmony_ci * @dev: the device to unregister 64262306a36Sopenharmony_ci * @bd: the backlight device to unregister 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * Deallocates a backlight allocated with devm_backlight_device_register(). 64562306a36Sopenharmony_ci * Normally this function will not need to be called and the resource management 64662306a36Sopenharmony_ci * code will ensure that the resources are freed. 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_civoid devm_backlight_device_unregister(struct device *dev, 64962306a36Sopenharmony_ci struct backlight_device *bd) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci int rc; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci rc = devres_release(dev, devm_backlight_device_release, 65462306a36Sopenharmony_ci devm_backlight_device_match, bd); 65562306a36Sopenharmony_ci WARN_ON(rc); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ciEXPORT_SYMBOL(devm_backlight_device_unregister); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci#ifdef CONFIG_OF 66062306a36Sopenharmony_cistatic int of_parent_match(struct device *dev, const void *data) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci return dev->parent && dev->parent->of_node == data; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/** 66662306a36Sopenharmony_ci * of_find_backlight_by_node() - find backlight device by device-tree node 66762306a36Sopenharmony_ci * @node: device-tree node of the backlight device 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci * Returns a pointer to the backlight device corresponding to the given DT 67062306a36Sopenharmony_ci * node or NULL if no such backlight device exists or if the device hasn't 67162306a36Sopenharmony_ci * been probed yet. 67262306a36Sopenharmony_ci * 67362306a36Sopenharmony_ci * This function obtains a reference on the backlight device and it is the 67462306a36Sopenharmony_ci * caller's responsibility to drop the reference by calling put_device() on 67562306a36Sopenharmony_ci * the backlight device's .dev field. 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_cistruct backlight_device *of_find_backlight_by_node(struct device_node *node) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct device *dev; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci dev = class_find_device(backlight_class, NULL, node, of_parent_match); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return dev ? to_backlight_device(dev) : NULL; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ciEXPORT_SYMBOL(of_find_backlight_by_node); 68662306a36Sopenharmony_ci#endif 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic struct backlight_device *of_find_backlight(struct device *dev) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct backlight_device *bd = NULL; 69162306a36Sopenharmony_ci struct device_node *np; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!dev) 69462306a36Sopenharmony_ci return NULL; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_OF) && dev->of_node) { 69762306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, "backlight", 0); 69862306a36Sopenharmony_ci if (np) { 69962306a36Sopenharmony_ci bd = of_find_backlight_by_node(np); 70062306a36Sopenharmony_ci of_node_put(np); 70162306a36Sopenharmony_ci if (!bd) 70262306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return bd; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void devm_backlight_release(void *data) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct backlight_device *bd = data; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci put_device(&bd->dev); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/** 71762306a36Sopenharmony_ci * devm_of_find_backlight - find backlight for a device 71862306a36Sopenharmony_ci * @dev: the device 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci * This function looks for a property named 'backlight' on the DT node 72162306a36Sopenharmony_ci * connected to @dev and looks up the backlight device. The lookup is 72262306a36Sopenharmony_ci * device managed so the reference to the backlight device is automatically 72362306a36Sopenharmony_ci * dropped on driver detach. 72462306a36Sopenharmony_ci * 72562306a36Sopenharmony_ci * RETURNS: 72662306a36Sopenharmony_ci * 72762306a36Sopenharmony_ci * A pointer to the backlight device if found. 72862306a36Sopenharmony_ci * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight 72962306a36Sopenharmony_ci * device is found. NULL if there's no backlight property. 73062306a36Sopenharmony_ci */ 73162306a36Sopenharmony_cistruct backlight_device *devm_of_find_backlight(struct device *dev) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct backlight_device *bd; 73462306a36Sopenharmony_ci int ret; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci bd = of_find_backlight(dev); 73762306a36Sopenharmony_ci if (IS_ERR_OR_NULL(bd)) 73862306a36Sopenharmony_ci return bd; 73962306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, devm_backlight_release, bd); 74062306a36Sopenharmony_ci if (ret) 74162306a36Sopenharmony_ci return ERR_PTR(ret); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return bd; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ciEXPORT_SYMBOL(devm_of_find_backlight); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic void __exit backlight_class_exit(void) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci class_destroy(backlight_class); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic int __init backlight_class_init(void) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci backlight_class = class_create("backlight"); 75562306a36Sopenharmony_ci if (IS_ERR(backlight_class)) { 75662306a36Sopenharmony_ci pr_warn("Unable to create backlight class; errno = %ld\n", 75762306a36Sopenharmony_ci PTR_ERR(backlight_class)); 75862306a36Sopenharmony_ci return PTR_ERR(backlight_class); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci backlight_class->dev_groups = bl_device_groups; 76262306a36Sopenharmony_ci backlight_class->pm = &backlight_class_dev_pm_ops; 76362306a36Sopenharmony_ci INIT_LIST_HEAD(&backlight_dev_list); 76462306a36Sopenharmony_ci mutex_init(&backlight_dev_list_mutex); 76562306a36Sopenharmony_ci BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/* 77162306a36Sopenharmony_ci * if this is compiled into the kernel, we need to ensure that the 77262306a36Sopenharmony_ci * class is registered before users of the class try to register lcd's 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_cipostcore_initcall(backlight_class_init); 77562306a36Sopenharmony_cimodule_exit(backlight_class_exit); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 77862306a36Sopenharmony_ciMODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>"); 77962306a36Sopenharmony_ciMODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction"); 780