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