| 1 | |
| 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)(&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)(&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(&bd->dev.kobj, KOBJ_CHANGE, envp);
|
| 134 | sysfs_notify(&bd->dev.kobj, NULL, "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 sysfs_emit(buf, "%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(buf, 0, &power);
|
| 153 | if (rc)
|
| 154 | return rc;
|
| 155 |
|
| 156 | rc = -ENXIO;
|
| 157 | mutex_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(&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 sysfs_emit(buf, "%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(&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(&bd->ops_lock);
|
| 202 |
|
| 203 | backlight_generate_event(bd, 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(buf, 0, &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 sysfs_emit(buf, "%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 sysfs_emit(buf, "%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(&bd->ops_lock);
|
| 251 | if (bd->ops && bd->ops->get_brightness) {
|
| 252 | rc = bd->ops->get_brightness(bd);
|
| 253 | if (rc >= 0)
|
| 254 | rc = sysfs_emit(buf, "%d\n", rc);
|
| 255 | } else {
|
| 256 | rc = sysfs_emit(buf, "%d\n", bd->props.brightness);
|
| 257 | }
|
| 258 | mutex_unlock(&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 sysfs_emit(buf, "unknown\n");
|
| 271 |
|
| 272 | return sysfs_emit(buf, "%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(&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(&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(&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(&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(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(&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(&bd->ops_lock);
|
| 360 | backlight_generate_event(bd, reason);
|
| 361 | }
|
| 362 | EXPORT_SYMBOL(backlight_force_update);
|
| 363 |
|
| 364 |
|
| 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_obj(struct backlight_device);
|
| 375 | if (!new_bd)
|
| 376 | return ERR_PTR(-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(&new_bd->dev, "%s", name);
|
| 385 | dev_set_drvdata(&new_bd->dev, devdata);
|
| 386 |
|
| 387 |
|
| 388 | if (props) {
|
| 389 | memcpy(&new_bd->props, props,
|
| 390 | 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(&new_bd->dev);
|
| 400 | if (rc) {
|
| 401 | put_device(&new_bd->dev);
|
| 402 | return ERR_PTR(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(&backlight_dev_list_mutex);
|
| 415 | list_add(&new_bd->entry, &backlight_dev_list);
|
| 416 | mutex_unlock(&backlight_dev_list_mutex);
|
| 417 |
|
| 418 | return new_bd;
|
| 419 | }
|
| 420 | EXPORT_SYMBOL(backlight_device_register);
|
| 421 |
|
| 422 | |
| 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(&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(&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(&backlight_class, name);
|
| 465 |
|
| 466 | return dev ? to_backlight_device(dev) : NULL;
|
| 467 | }
|
| 468 | EXPORT_SYMBOL(backlight_device_get_by_name);
|
| 469 |
|
| 470 |
|
| 471 | void backlight_device_unregister(struct backlight_device *bd)
|
| 472 | {
|
| 473 | if (!bd)
|
| 474 | return;
|
| 475 |
|
| 476 | mutex_lock(&backlight_dev_list_mutex);
|
| 477 | list_del(&bd->entry);
|
| 478 | mutex_unlock(&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(&bd->ops_lock);
|
| 488 | bd->ops = NULL;
|
| 489 | mutex_unlock(&bd->ops_lock);
|
| 490 |
|
| 491 | device_unregister(&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(-ENOMEM);
|
| 538 |
|
| 539 | backlight = backlight_device_register(name, parent, devdata, ops,
|
| 540 | props);
|
| 541 | if (!IS_ERR(backlight)) {
|
| 542 | *ptr = backlight;
|
| 543 | devres_add(dev, ptr);
|
| 544 | } else {
|
| 545 | devres_free(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, devm_backlight_device_release,
|
| 567 | devm_backlight_device_match, 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(dev->of_node, "backlight", 0);
|
| 611 | if (np) {
|
| 612 | bd = of_find_backlight_by_node(np);
|
| 613 | of_node_put(np);
|
| 614 | if (!bd)
|
| 615 | return ERR_PTR(-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(&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(bd))
|
| 651 | return bd;
|
| 652 | ret = devm_add_action_or_reset(dev, devm_backlight_release, bd);
|
| 653 | if (ret)
|
| 654 | return ERR_PTR(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(&backlight_class);
|
| 663 | }
|
| 664 |
|
| 665 | static int __init backlight_class_init(void)
|
| 666 | {
|
| 667 | int ret;
|
| 668 |
|
| 669 | ret = class_register(&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(&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 <[email protected]>, Andrew Zabolotny <[email protected]>");
|
| 690 | MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
|