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