1 /* 2 * LCD Lowlevel Control Abstraction 3 * 4 * Copyright (C) 2003,2004 Hewlett-Packard Company 5 * 6 */ 7 8 #include <linux/version.h> 9 #include <linux/module.h> 10 #include <linux/init.h> 11 #include <linux/device.h> 12 #include <linux/lcd.h> 13 #include <linux/notifier.h> 14 #include <linux/ctype.h> 15 #include <linux/err.h> 16 #include <linux/fb.h> 17 #include <asm/bug.h> 18 19 static ssize_t lcd_show_power(struct class_device *cdev, char *buf) 20 { 21 int rc; 22 struct lcd_device *ld = to_lcd_device(cdev); 23 24 down(&ld->sem); 25 if (likely(ld->props && ld->props->get_power)) 26 rc = sprintf(buf, "%d\n", ld->props->get_power(ld)); 27 else 28 rc = -ENXIO; 29 up(&ld->sem); 30 31 return rc; 32 } 33 34 static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_t count) 35 { 36 int rc, power; 37 char *endp; 38 struct lcd_device *ld = to_lcd_device(cdev); 39 40 power = simple_strtoul(buf, &endp, 0); 41 if (*endp && !isspace(*endp)) 42 return -EINVAL; 43 44 down(&ld->sem); 45 if (likely(ld->props && ld->props->set_power)) { 46 pr_debug("lcd: set power to %d\n", power); 47 ld->props->set_power(ld, power); 48 rc = count; 49 } else 50 rc = -ENXIO; 51 up(&ld->sem); 52 53 return rc; 54 } 55 56 static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf) 57 { 58 int rc; 59 struct lcd_device *ld = to_lcd_device(cdev); 60 61 down(&ld->sem); 62 if (likely(ld->props && ld->props->get_contrast)) 63 rc = sprintf(buf, "%d\n", ld->props->get_contrast(ld)); 64 else 65 rc = -ENXIO; 66 up(&ld->sem); 67 68 return rc; 69 } 70 71 static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, size_t count) 72 { 73 int rc, contrast; 74 char *endp; 75 struct lcd_device *ld = to_lcd_device(cdev); 76 77 contrast = simple_strtoul(buf, &endp, 0); 78 if (*endp && !isspace(*endp)) 79 return -EINVAL; 80 81 down(&ld->sem); 82 if (likely(ld->props && ld->props->set_contrast)) { 83 pr_debug("lcd: set contrast to %d\n", contrast); 84 ld->props->set_contrast(ld, contrast); 85 rc = count; 86 } else 87 rc = -ENXIO; 88 up(&ld->sem); 89 90 return rc; 91 } 92 93 static ssize_t lcd_show_max_contrast(struct class_device *cdev, char *buf) 94 { 95 int rc; 96 struct lcd_device *ld = to_lcd_device(cdev); 97 98 down(&ld->sem); 99 if (likely(ld->props)) 100 rc = sprintf(buf, "%d\n", ld->props->max_contrast); 101 else 102 rc = -ENXIO; 103 up(&ld->sem); 104 105 return rc; 106 } 107 108 static void lcd_class_release(struct class_device *dev) 109 { 110 struct lcd_device *ld = to_lcd_device(dev); 111 kfree(ld); 112 } 113 114 static struct class lcd_class = { 115 .name = "lcd", 116 .release = lcd_class_release, 117 }; 118 119 #define DECLARE_ATTR(_name,_mode,_show,_store) \ 120 { \ 121 .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ 122 .show = _show, \ 123 .store = _store, \ 124 } 125 126 static struct class_device_attribute lcd_class_device_attributes[] = { 127 DECLARE_ATTR(power, 0644, lcd_show_power, lcd_store_power), 128 DECLARE_ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast), 129 DECLARE_ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL), 130 }; 131 132 /* This callback gets called when something important happens inside a 133 * framebuffer driver. We're looking if that important event is blanking, 134 * and if it is, we're switching lcd power as well ... 135 */ 136 static int fb_notifier_callback(struct notifier_block *self, 137 unsigned long event, void *data) 138 { 139 struct lcd_device *ld; 140 struct fb_event *evdata =(struct fb_event *)data; 141 142 /* If we aren't interested in this event, skip it immediately ... */ 143 if (event != FB_EVENT_BLANK) 144 return 0; 145 146 ld = container_of(self, struct lcd_device, fb_notif); 147 down(&ld->sem); 148 if (ld->props) 149 if (!ld->props->check_fb || ld->props->check_fb(evdata->info)) 150 ld->props->set_power(ld, *(int *)evdata->data); 151 up(&ld->sem); 152 return 0; 153 } 154 155 /** 156 * lcd_device_register - register a new object of lcd_device class. 157 * @name: the name of the new object(must be the same as the name of the 158 * respective framebuffer device). 159 * @devdata: an optional pointer to be stored in the class_device. The 160 * methods may retrieve it by using class_get_devdata(ld->class_dev). 161 * @lp: the lcd properties structure. 162 * 163 * Creates and registers a new lcd class_device. Returns either an ERR_PTR() 164 * or a pointer to the newly allocated device. 165 */ 166 struct lcd_device *lcd_device_register(const char *name, void *devdata, 167 struct lcd_properties *lp) 168 { 169 int i, rc; 170 struct lcd_device *new_ld; 171 172 pr_debug("lcd_device_register: name=%s\n", name); 173 174 new_ld = kmalloc(sizeof(struct lcd_device), GFP_KERNEL); 175 if (unlikely(!new_ld)) 176 return ERR_PTR(ENOMEM); 177 178 init_MUTEX(&new_ld->sem); 179 new_ld->props = lp; 180 memset(&new_ld->class_dev, 0, sizeof(new_ld->class_dev)); 181 new_ld->class_dev.class = &lcd_class; 182 strlcpy(new_ld->class_dev.class_id, name, KOBJ_NAME_LEN); 183 class_set_devdata(&new_ld->class_dev, devdata); 184 185 rc = class_device_register(&new_ld->class_dev); 186 if (unlikely(rc)) { 187 error: kfree(new_ld); 188 return ERR_PTR(rc); 189 } 190 191 memset(&new_ld->fb_notif, 0, sizeof(new_ld->fb_notif)); 192 new_ld->fb_notif.notifier_call = fb_notifier_callback; 193 194 rc = fb_register_client(&new_ld->fb_notif); 195 if (unlikely(rc)) 196 goto error; 197 198 for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) { 199 rc = class_device_create_file(&new_ld->class_dev, 200 &lcd_class_device_attributes[i]); 201 if (unlikely(rc)) { 202 while (--i >= 0) 203 class_device_remove_file(&new_ld->class_dev, 204 &lcd_class_device_attributes[i]); 205 class_device_unregister(&new_ld->class_dev); 206 /* No need to kfree(new_ld) since release() method was called */ 207 return ERR_PTR(rc); 208 } 209 } 210 211 return new_ld; 212 } 213 EXPORT_SYMBOL(lcd_device_register); 214 215 /** 216 * lcd_device_unregister - unregisters a object of lcd_device class. 217 * @ld: the lcd device object to be unregistered and freed. 218 * 219 * Unregisters a previously registered via lcd_device_register object. 220 */ 221 void lcd_device_unregister(struct lcd_device *ld) 222 { 223 int i; 224 225 if (!ld) 226 return; 227 228 pr_debug("lcd_device_unregister: name=%s\n", ld->class_dev.class_id); 229 230 for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) 231 class_device_remove_file(&ld->class_dev, 232 &lcd_class_device_attributes[i]); 233 234 down(&ld->sem); 235 ld->props = NULL; 236 up(&ld->sem); 237 238 fb_unregister_client(&ld->fb_notif); 239 240 class_device_unregister(&ld->class_dev); 241 } 242 EXPORT_SYMBOL(lcd_device_unregister); 243 244 static void __exit lcd_class_exit(void) 245 { 246 class_unregister(&lcd_class); 247 } 248 249 static int __init lcd_class_init(void) 250 { 251 return class_register(&lcd_class); 252 } 253 254 /* 255 * if this is compiled into the kernel, we need to ensure that the 256 * class is registered before users of the class try to register lcd's 257 */ 258 postcore_initcall(lcd_class_init); 259 module_exit(lcd_class_exit); 260 261 MODULE_LICENSE("GPL"); 262 MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>"); 263 MODULE_DESCRIPTION("LCD Lowlevel Control Abstraction"); 264