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