linux/drivers/video/backlight/backlight.c
<<
>>
Prefs
   1/*
   2 * Backlight Lowlevel Control Abstraction
   3 *
   4 * Copyright (C) 2003,2004 Hewlett-Packard Company
   5 *
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/init.h>
  10#include <linux/device.h>
  11#include <linux/backlight.h>
  12#include <linux/notifier.h>
  13#include <linux/ctype.h>
  14#include <linux/err.h>
  15#include <linux/fb.h>
  16#include <linux/slab.h>
  17
  18#ifdef CONFIG_PMAC_BACKLIGHT
  19#include <asm/backlight.h>
  20#endif
  21
  22#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
  23                           defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
  24/* This callback gets called when something important happens inside a
  25 * framebuffer driver. We're looking if that important event is blanking,
  26 * and if it is, we're switching backlight power as well ...
  27 */
  28static int fb_notifier_callback(struct notifier_block *self,
  29                                unsigned long event, void *data)
  30{
  31        struct backlight_device *bd;
  32        struct fb_event *evdata = data;
  33
  34        /* If we aren't interested in this event, skip it immediately ... */
  35        if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
  36                return 0;
  37
  38        bd = container_of(self, struct backlight_device, fb_notif);
  39        mutex_lock(&bd->ops_lock);
  40        if (bd->ops)
  41                if (!bd->ops->check_fb ||
  42                    bd->ops->check_fb(bd, evdata->info)) {
  43                        bd->props.fb_blank = *(int *)evdata->data;
  44                        if (bd->props.fb_blank == FB_BLANK_UNBLANK)
  45                                bd->props.state &= ~BL_CORE_FBBLANK;
  46                        else
  47                                bd->props.state |= BL_CORE_FBBLANK;
  48                        backlight_update_status(bd);
  49                }
  50        mutex_unlock(&bd->ops_lock);
  51        return 0;
  52}
  53
  54static int backlight_register_fb(struct backlight_device *bd)
  55{
  56        memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));
  57        bd->fb_notif.notifier_call = fb_notifier_callback;
  58
  59        return fb_register_client(&bd->fb_notif);
  60}
  61
  62static void backlight_unregister_fb(struct backlight_device *bd)
  63{
  64        fb_unregister_client(&bd->fb_notif);
  65}
  66#else
  67static inline int backlight_register_fb(struct backlight_device *bd)
  68{
  69        return 0;
  70}
  71
  72static inline void backlight_unregister_fb(struct backlight_device *bd)
  73{
  74}
  75#endif /* CONFIG_FB */
  76
  77static void backlight_generate_event(struct backlight_device *bd,
  78                                     enum backlight_update_reason reason)
  79{
  80        char *envp[2];
  81
  82        switch (reason) {
  83        case BACKLIGHT_UPDATE_SYSFS:
  84                envp[0] = "SOURCE=sysfs";
  85                break;
  86        case BACKLIGHT_UPDATE_HOTKEY:
  87                envp[0] = "SOURCE=hotkey";
  88                break;
  89        default:
  90                envp[0] = "SOURCE=unknown";
  91                break;
  92        }
  93        envp[1] = NULL;
  94        kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);
  95        sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
  96}
  97
  98static ssize_t backlight_show_power(struct device *dev,
  99                struct device_attribute *attr,char *buf)
 100{
 101        struct backlight_device *bd = to_backlight_device(dev);
 102
 103        return sprintf(buf, "%d\n", bd->props.power);
 104}
 105
 106static ssize_t backlight_store_power(struct device *dev,
 107                struct device_attribute *attr, const char *buf, size_t count)
 108{
 109        int rc;
 110        struct backlight_device *bd = to_backlight_device(dev);
 111        unsigned long power;
 112
 113        rc = strict_strtoul(buf, 0, &power);
 114        if (rc)
 115                return rc;
 116
 117        rc = -ENXIO;
 118        mutex_lock(&bd->ops_lock);
 119        if (bd->ops) {
 120                pr_debug("backlight: set power to %lu\n", power);
 121                if (bd->props.power != power) {
 122                        bd->props.power = power;
 123                        backlight_update_status(bd);
 124                }
 125                rc = count;
 126        }
 127        mutex_unlock(&bd->ops_lock);
 128
 129        return rc;
 130}
 131
 132static ssize_t backlight_show_brightness(struct device *dev,
 133                struct device_attribute *attr, char *buf)
 134{
 135        struct backlight_device *bd = to_backlight_device(dev);
 136
 137        return sprintf(buf, "%d\n", bd->props.brightness);
 138}
 139
 140static ssize_t backlight_store_brightness(struct device *dev,
 141                struct device_attribute *attr, const char *buf, size_t count)
 142{
 143        int rc;
 144        struct backlight_device *bd = to_backlight_device(dev);
 145        unsigned long brightness;
 146
 147        rc = strict_strtoul(buf, 0, &brightness);
 148        if (rc)
 149                return rc;
 150
 151        rc = -ENXIO;
 152
 153        mutex_lock(&bd->ops_lock);
 154        if (bd->ops) {
 155                if (brightness > bd->props.max_brightness)
 156                        rc = -EINVAL;
 157                else {
 158                        pr_debug("backlight: set brightness to %lu\n",
 159                                 brightness);
 160                        bd->props.brightness = brightness;
 161                        backlight_update_status(bd);
 162                        rc = count;
 163                }
 164        }
 165        mutex_unlock(&bd->ops_lock);
 166
 167        backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
 168
 169        return rc;
 170}
 171
 172static ssize_t backlight_show_max_brightness(struct device *dev,
 173                struct device_attribute *attr, char *buf)
 174{
 175        struct backlight_device *bd = to_backlight_device(dev);
 176
 177        return sprintf(buf, "%d\n", bd->props.max_brightness);
 178}
 179
 180static ssize_t backlight_show_actual_brightness(struct device *dev,
 181                struct device_attribute *attr, char *buf)
 182{
 183        int rc = -ENXIO;
 184        struct backlight_device *bd = to_backlight_device(dev);
 185
 186        mutex_lock(&bd->ops_lock);
 187        if (bd->ops && bd->ops->get_brightness)
 188                rc = sprintf(buf, "%d\n", bd->ops->get_brightness(bd));
 189        mutex_unlock(&bd->ops_lock);
 190
 191        return rc;
 192}
 193
 194static struct class *backlight_class;
 195
 196static int backlight_suspend(struct device *dev, pm_message_t state)
 197{
 198        struct backlight_device *bd = to_backlight_device(dev);
 199
 200        mutex_lock(&bd->ops_lock);
 201        if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
 202                bd->props.state |= BL_CORE_SUSPENDED;
 203                backlight_update_status(bd);
 204        }
 205        mutex_unlock(&bd->ops_lock);
 206
 207        return 0;
 208}
 209
 210static int backlight_resume(struct device *dev)
 211{
 212        struct backlight_device *bd = to_backlight_device(dev);
 213
 214        mutex_lock(&bd->ops_lock);
 215        if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
 216                bd->props.state &= ~BL_CORE_SUSPENDED;
 217                backlight_update_status(bd);
 218        }
 219        mutex_unlock(&bd->ops_lock);
 220
 221        return 0;
 222}
 223
 224static void bl_device_release(struct device *dev)
 225{
 226        struct backlight_device *bd = to_backlight_device(dev);
 227        kfree(bd);
 228}
 229
 230static struct device_attribute bl_device_attributes[] = {
 231        __ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
 232        __ATTR(brightness, 0644, backlight_show_brightness,
 233                     backlight_store_brightness),
 234        __ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
 235                     NULL),
 236        __ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
 237        __ATTR_NULL,
 238};
 239
 240/**
 241 * backlight_force_update - tell the backlight subsystem that hardware state
 242 *   has changed
 243 * @bd: the backlight device to update
 244 *
 245 * Updates the internal state of the backlight in response to a hardware event,
 246 * and generate a uevent to notify userspace
 247 */
 248void backlight_force_update(struct backlight_device *bd,
 249                            enum backlight_update_reason reason)
 250{
 251        mutex_lock(&bd->ops_lock);
 252        if (bd->ops && bd->ops->get_brightness)
 253                bd->props.brightness = bd->ops->get_brightness(bd);
 254        mutex_unlock(&bd->ops_lock);
 255        backlight_generate_event(bd, reason);
 256}
 257EXPORT_SYMBOL(backlight_force_update);
 258
 259/**
 260 * backlight_device_register - create and register a new object of
 261 *   backlight_device class.
 262 * @name: the name of the new object(must be the same as the name of the
 263 *   respective framebuffer device).
 264 * @parent: a pointer to the parent device
 265 * @devdata: an optional pointer to be stored for private driver use. The
 266 *   methods may retrieve it by using bl_get_data(bd).
 267 * @ops: the backlight operations structure.
 268 *
 269 * Creates and registers new backlight device. Returns either an
 270 * ERR_PTR() or a pointer to the newly allocated device.
 271 */
 272struct backlight_device *backlight_device_register(const char *name,
 273        struct device *parent, void *devdata, const struct backlight_ops *ops,
 274        const struct backlight_properties *props)
 275{
 276        struct backlight_device *new_bd;
 277        int rc;
 278
 279        pr_debug("backlight_device_register: name=%s\n", name);
 280
 281        new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
 282        if (!new_bd)
 283                return ERR_PTR(-ENOMEM);
 284
 285        mutex_init(&new_bd->update_lock);
 286        mutex_init(&new_bd->ops_lock);
 287
 288        new_bd->dev.class = backlight_class;
 289        new_bd->dev.parent = parent;
 290        new_bd->dev.release = bl_device_release;
 291        dev_set_name(&new_bd->dev, name);
 292        dev_set_drvdata(&new_bd->dev, devdata);
 293
 294        /* Set default properties */
 295        if (props)
 296                memcpy(&new_bd->props, props,
 297                       sizeof(struct backlight_properties));
 298
 299        rc = device_register(&new_bd->dev);
 300        if (rc) {
 301                kfree(new_bd);
 302                return ERR_PTR(rc);
 303        }
 304
 305        rc = backlight_register_fb(new_bd);
 306        if (rc) {
 307                device_unregister(&new_bd->dev);
 308                return ERR_PTR(rc);
 309        }
 310
 311        new_bd->ops = ops;
 312
 313#ifdef CONFIG_PMAC_BACKLIGHT
 314        mutex_lock(&pmac_backlight_mutex);
 315        if (!pmac_backlight)
 316                pmac_backlight = new_bd;
 317        mutex_unlock(&pmac_backlight_mutex);
 318#endif
 319
 320        return new_bd;
 321}
 322EXPORT_SYMBOL(backlight_device_register);
 323
 324/**
 325 * backlight_device_unregister - unregisters a backlight device object.
 326 * @bd: the backlight device object to be unregistered and freed.
 327 *
 328 * Unregisters a previously registered via backlight_device_register object.
 329 */
 330void backlight_device_unregister(struct backlight_device *bd)
 331{
 332        if (!bd)
 333                return;
 334
 335#ifdef CONFIG_PMAC_BACKLIGHT
 336        mutex_lock(&pmac_backlight_mutex);
 337        if (pmac_backlight == bd)
 338                pmac_backlight = NULL;
 339        mutex_unlock(&pmac_backlight_mutex);
 340#endif
 341        mutex_lock(&bd->ops_lock);
 342        bd->ops = NULL;
 343        mutex_unlock(&bd->ops_lock);
 344
 345        backlight_unregister_fb(bd);
 346        device_unregister(&bd->dev);
 347}
 348EXPORT_SYMBOL(backlight_device_unregister);
 349
 350static void __exit backlight_class_exit(void)
 351{
 352        class_destroy(backlight_class);
 353}
 354
 355static int __init backlight_class_init(void)
 356{
 357        backlight_class = class_create(THIS_MODULE, "backlight");
 358        if (IS_ERR(backlight_class)) {
 359                printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
 360                                PTR_ERR(backlight_class));
 361                return PTR_ERR(backlight_class);
 362        }
 363
 364        backlight_class->dev_attrs = bl_device_attributes;
 365        backlight_class->suspend = backlight_suspend;
 366        backlight_class->resume = backlight_resume;
 367        return 0;
 368}
 369
 370/*
 371 * if this is compiled into the kernel, we need to ensure that the
 372 * class is registered before users of the class try to register lcd's
 373 */
 374postcore_initcall(backlight_class_init);
 375module_exit(backlight_class_exit);
 376
 377MODULE_LICENSE("GPL");
 378MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
 379MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
 380