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 static ssize_t backlight_show_power(struct class_device *cdev, char *buf) 18 { 19 int rc = -ENXIO; 20 struct backlight_device *bd = to_backlight_device(cdev); 21 22 down(&bd->sem); 23 if (likely(bd->props)) 24 rc = sprintf(buf, "%d\n", bd->props->power); 25 up(&bd->sem); 26 27 return rc; 28 } 29 30 static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count) 31 { 32 int rc = -ENXIO; 33 char *endp; 34 struct backlight_device *bd = to_backlight_device(cdev); 35 int power = simple_strtoul(buf, &endp, 0); 36 size_t size = endp - buf; 37 38 if (*endp && isspace(*endp)) 39 size++; 40 if (size != count) 41 return -EINVAL; 42 43 down(&bd->sem); 44 if (likely(bd->props)) { 45 pr_debug("backlight: set power to %d\n", power); 46 bd->props->power = power; 47 if (likely(bd->props->update_status)) 48 bd->props->update_status(bd); 49 rc = count; 50 } 51 up(&bd->sem); 52 53 return rc; 54 } 55 56 static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf) 57 { 58 int rc = -ENXIO; 59 struct backlight_device *bd = to_backlight_device(cdev); 60 61 down(&bd->sem); 62 if (likely(bd->props)) 63 rc = sprintf(buf, "%d\n", bd->props->brightness); 64 up(&bd->sem); 65 66 return rc; 67 } 68 69 static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count) 70 { 71 int rc = -ENXIO; 72 char *endp; 73 struct backlight_device *bd = to_backlight_device(cdev); 74 int brightness = simple_strtoul(buf, &endp, 0); 75 size_t size = endp - buf; 76 77 if (*endp && isspace(*endp)) 78 size++; 79 if (size != count) 80 return -EINVAL; 81 82 down(&bd->sem); 83 if (likely(bd->props)) { 84 if (brightness > bd->props->max_brightness) 85 rc = -EINVAL; 86 else { 87 pr_debug("backlight: set brightness to %d\n", 88 brightness); 89 bd->props->brightness = brightness; 90 if (likely(bd->props->update_status)) 91 bd->props->update_status(bd); 92 rc = count; 93 } 94 } 95 up(&bd->sem); 96 97 return rc; 98 } 99 100 static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf) 101 { 102 int rc = -ENXIO; 103 struct backlight_device *bd = to_backlight_device(cdev); 104 105 down(&bd->sem); 106 if (likely(bd->props)) 107 rc = sprintf(buf, "%d\n", bd->props->max_brightness); 108 up(&bd->sem); 109 110 return rc; 111 } 112 113 static ssize_t backlight_show_actual_brightness(struct class_device *cdev, 114 char *buf) 115 { 116 int rc = -ENXIO; 117 struct backlight_device *bd = to_backlight_device(cdev); 118 119 down(&bd->sem); 120 if (likely(bd->props && bd->props->get_brightness)) 121 rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd)); 122 up(&bd->sem); 123 124 return rc; 125 } 126 127 static void backlight_class_release(struct class_device *dev) 128 { 129 struct backlight_device *bd = to_backlight_device(dev); 130 kfree(bd); 131 } 132 133 static struct class backlight_class = { 134 .name = "backlight", 135 .release = backlight_class_release, 136 }; 137 138 #define DECLARE_ATTR(_name,_mode,_show,_store) \ 139 { \ 140 .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ 141 .show = _show, \ 142 .store = _store, \ 143 } 144 145 static struct class_device_attribute bl_class_device_attributes[] = { 146 DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power), 147 DECLARE_ATTR(brightness, 0644, backlight_show_brightness, 148 backlight_store_brightness), 149 DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness, 150 NULL), 151 DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL), 152 }; 153 154 /* This callback gets called when something important happens inside a 155 * framebuffer driver. We're looking if that important event is blanking, 156 * and if it is, we're switching backlight power as well ... 157 */ 158 static int fb_notifier_callback(struct notifier_block *self, 159 unsigned long event, void *data) 160 { 161 struct backlight_device *bd; 162 struct fb_event *evdata =(struct fb_event *)data; 163 164 /* If we aren't interested in this event, skip it immediately ... */ 165 if (event != FB_EVENT_BLANK) 166 return 0; 167 168 bd = container_of(self, struct backlight_device, fb_notif); 169 down(&bd->sem); 170 if (bd->props) 171 if (!bd->props->check_fb || 172 bd->props->check_fb(evdata->info)) { 173 bd->props->fb_blank = *(int *)evdata->data; 174 if (likely(bd->props && bd->props->update_status)) 175 bd->props->update_status(bd); 176 } 177 up(&bd->sem); 178 return 0; 179 } 180 181 /** 182 * backlight_device_register - create and register a new object of 183 * backlight_device class. 184 * @name: the name of the new object(must be the same as the name of the 185 * respective framebuffer device). 186 * @devdata: an optional pointer to be stored in the class_device. The 187 * methods may retrieve it by using class_get_devdata(&bd->class_dev). 188 * @bp: the backlight properties structure. 189 * 190 * Creates and registers new backlight class_device. Returns either an 191 * ERR_PTR() or a pointer to the newly allocated device. 192 */ 193 struct backlight_device *backlight_device_register(const char *name, void *devdata, 194 struct backlight_properties *bp) 195 { 196 int i, rc; 197 struct backlight_device *new_bd; 198 199 pr_debug("backlight_device_alloc: name=%s\n", name); 200 201 new_bd = kmalloc(sizeof(struct backlight_device), GFP_KERNEL); 202 if (unlikely(!new_bd)) 203 return ERR_PTR(-ENOMEM); 204 205 init_MUTEX(&new_bd->sem); 206 new_bd->props = bp; 207 memset(&new_bd->class_dev, 0, sizeof(new_bd->class_dev)); 208 new_bd->class_dev.class = &backlight_class; 209 strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN); 210 class_set_devdata(&new_bd->class_dev, devdata); 211 212 rc = class_device_register(&new_bd->class_dev); 213 if (unlikely(rc)) { 214 error: kfree(new_bd); 215 return ERR_PTR(rc); 216 } 217 218 memset(&new_bd->fb_notif, 0, sizeof(new_bd->fb_notif)); 219 new_bd->fb_notif.notifier_call = fb_notifier_callback; 220 221 rc = fb_register_client(&new_bd->fb_notif); 222 if (unlikely(rc)) 223 goto error; 224 225 for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) { 226 rc = class_device_create_file(&new_bd->class_dev, 227 &bl_class_device_attributes[i]); 228 if (unlikely(rc)) { 229 while (--i >= 0) 230 class_device_remove_file(&new_bd->class_dev, 231 &bl_class_device_attributes[i]); 232 class_device_unregister(&new_bd->class_dev); 233 /* No need to kfree(new_bd) since release() method was called */ 234 return ERR_PTR(rc); 235 } 236 } 237 238 return new_bd; 239 } 240 EXPORT_SYMBOL(backlight_device_register); 241 242 /** 243 * backlight_device_unregister - unregisters a backlight device object. 244 * @bd: the backlight device object to be unregistered and freed. 245 * 246 * Unregisters a previously registered via backlight_device_register object. 247 */ 248 void backlight_device_unregister(struct backlight_device *bd) 249 { 250 int i; 251 252 if (!bd) 253 return; 254 255 pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id); 256 257 for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) 258 class_device_remove_file(&bd->class_dev, 259 &bl_class_device_attributes[i]); 260 261 down(&bd->sem); 262 if (likely(bd->props && bd->props->update_status)) { 263 bd->props->brightness = 0; 264 bd->props->power = 0; 265 bd->props->update_status(bd); 266 } 267 268 bd->props = NULL; 269 up(&bd->sem); 270 271 fb_unregister_client(&bd->fb_notif); 272 273 class_device_unregister(&bd->class_dev); 274 } 275 EXPORT_SYMBOL(backlight_device_unregister); 276 277 static void __exit backlight_class_exit(void) 278 { 279 class_unregister(&backlight_class); 280 } 281 282 static int __init backlight_class_init(void) 283 { 284 return class_register(&backlight_class); 285 } 286 287 /* 288 * if this is compiled into the kernel, we need to ensure that the 289 * class is registered before users of the class try to register lcd's 290 */ 291 postcore_initcall(backlight_class_init); 292 module_exit(backlight_class_exit); 293 294 MODULE_LICENSE("GPL"); 295 MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>"); 296 MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction"); 297