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