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