# # Patch managed by http://www.holgerschurig.de/patcher.html # --- /dev/null +++ linux/include/linux/lcd.h @@ -0,0 +1,52 @@ +/* + * linux/include/lcd.h + * + * Copyright (C) 2004 John Lenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * LCD Lowlevel Control Abstraction + * + * Based on a patch by Andrew Zabolotny and Jamey Hicks + */ + +#include + +struct lcd_class_device; + +enum lcd_power_status { + lcd_power_full_on = 0, + lcd_power_full_off, + lcd_power_controller_on, + lcd_power_flatpanel_off, +}; + +struct lcd_properties { + /* owner module */ + struct module *owner; + + /* device of this lcd */ + struct device *lcd_dev; + + /* get and set the current power state */ + enum lcd_power_status (*get_power) (struct lcd_properties *props); + void (*set_power) (struct lcd_properties *props, enum lcd_power_status power); + + /* The maximum value for contrast (read-only) */ + int max_contrast; + + /* get and set the current contrast level. */ + int (*get_contrast) (struct lcd_properties *props); + void (*set_contrast) (struct lcd_properties *props, int contrast); + + /* match with a given framebuffer device */ + int (*match) (struct lcd_properties *props, struct fb_info *fb_info); + + /* private */ + struct lcd_class_device *lcd_class_dev; +}; + +int lcd_device_register(struct lcd_properties *props, const char *name); +void lcd_device_unregister(struct lcd_properties *props); --- /dev/null +++ linux/drivers/video/lcd.c @@ -0,0 +1,312 @@ +/* + * linux/drivers/video/lcd.c + * + * Copyright (C) 2004 John Lenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on a patch by Andrew Zabolotny and Jamey Hicks + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct lcd_class_device { + /* This protects the props field.*/ + spinlock_t lock; + /* If props is NULL, the driver that registered this device has been unloaded */ + struct lcd_properties *props; + struct class_device class_dev; + + struct list_head list; +}; +#define to_lcd_device(d) container_of(d, struct lcd_class_device, class_dev) + +struct lcd_fb_list_node { + struct list_head list; + struct fb_info *fb_info; +}; +static spinlock_t lcd_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(lcd_fb_list); +static LIST_HEAD(lcd_device_list); + +struct lcd_power_name { + const char * name; + enum lcd_power_status power; +}; +static const struct lcd_power_name lcd_power_names[] = { + { "full on", lcd_power_full_on }, + { "full off", lcd_power_full_off }, + { "controller on", lcd_power_controller_on }, + { "flatpanel off", lcd_power_flatpanel_off }, +}; + +static void lcd_class_release(struct class_device *dev) +{ + struct lcd_class_device *d = to_lcd_device(dev); + kfree(d); +} + +static struct class lcd_class = { + .name = "lcd", + .release = lcd_class_release, +}; + +static ssize_t lcd_show_power(struct class_device *dev, char *buf) +{ + struct lcd_class_device *lcd_dev = to_lcd_device(dev); + ssize_t ret = 0; + + spin_lock(&lcd_dev->lock); + if (likely(lcd_dev->props)) { + if (lcd_dev->props->get_power) { + sprintf(buf, "%s\n", lcd_power_names[(int)lcd_dev->props->get_power(lcd_dev->props)].name); + ret = strlen(buf) + 1; + } + } + spin_unlock(&lcd_dev->lock); + + return ret; +} + +static ssize_t lcd_store_power(struct class_device *dev, const char *buf, size_t size) +{ + struct lcd_class_device *lcd_dev = to_lcd_device(dev); + int ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(lcd_power_names); i++) { + if (strncmp(buf, lcd_power_names[i].name, strlen(lcd_power_names[i].name)) == 0) { + ret = size; + spin_lock(&lcd_dev->lock); + if (likely(lcd_dev->props)) { + if (lcd_dev->props->set_power) { + lcd_dev->props->set_power(lcd_dev->props, lcd_power_names[i].power); + } + } + spin_unlock(&lcd_dev->lock); + break; + } + } + + return ret; +} + +static CLASS_DEVICE_ATTR(power, 0644, lcd_show_power, lcd_store_power); + +static ssize_t lcd_show_maxcontrast(struct class_device *dev, char *buf) +{ + struct lcd_class_device *lcd_dev = to_lcd_device(dev); + ssize_t ret = 0; + + spin_lock(&lcd_dev->lock); + if (likely(lcd_dev->props)) { + sprintf(buf, "%i\n", lcd_dev->props->max_contrast); + ret = strlen(buf) + 1; + } + spin_unlock(&lcd_dev->lock); + + return ret; +} + +static CLASS_DEVICE_ATTR(max_contrast, 0444, lcd_show_maxcontrast, NULL); + +static ssize_t lcd_show_contrast(struct class_device *dev, char *buf) +{ + struct lcd_class_device *lcd_dev = to_lcd_device(dev); + ssize_t ret = 0; + + spin_lock(&lcd_dev->lock); + if (likely(lcd_dev->props)) { + if (lcd_dev->props->get_contrast) { + sprintf(buf, "%i\n", lcd_dev->props->get_contrast(lcd_dev->props)); + ret = strlen(buf) + 1; + } + } + spin_unlock(&lcd_dev->lock); + + return ret; +} + +static ssize_t lcd_store_contrast(struct class_device *dev, const char *buf, size_t size) +{ + struct lcd_class_device *lcd_dev = to_lcd_device(dev); + int ret = -EINVAL; + char *after; + + unsigned long contrast = simple_strtoul(buf, &after, 0); + if (after - buf > 0) { + ret = after - buf; + spin_lock(&lcd_dev->lock); + if (likely(lcd_dev->props)) { + if (lcd_dev->props->set_contrast) { + lcd_dev->props->set_contrast(lcd_dev->props, contrast); + } + } + spin_unlock(&lcd_dev->lock); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast); + +/** + * lcd_device_register - register a new object of lcd_device class. + * @prop: the lcd properties structure for this device. + */ +int lcd_device_register(struct lcd_properties *props, const char *name) +{ + int rc; + struct lcd_class_device *new_lcd; + struct lcd_fb_list_node *node; + + new_lcd = kmalloc (sizeof (struct lcd_class_device), GFP_KERNEL); + if (unlikely (!new_lcd)) + return -ENOMEM; + + spin_lock_init(&new_lcd->lock); + new_lcd->props = props; + props->lcd_class_dev = new_lcd; + + memset (&new_lcd->class_dev, 0, sizeof (new_lcd->class_dev)); + new_lcd->class_dev.class = &lcd_class; + new_lcd->class_dev.dev = props->lcd_dev; + strlcpy (new_lcd->class_dev.class_id, name, KOBJ_NAME_LEN); + + rc = class_device_register (&new_lcd->class_dev); + if (unlikely (rc)) { + kfree (new_lcd); + return rc; + } + + /* register the attributes */ + class_device_create_file(&new_lcd->class_dev, &class_device_attr_power); + class_device_create_file(&new_lcd->class_dev, &class_device_attr_max_contrast); + class_device_create_file(&new_lcd->class_dev, &class_device_attr_contrast); + + /* add to device list and attempt to match with fb devices */ + spin_lock(&lcd_list_lock); + list_add_tail(&lcd_device_list, &new_lcd->list); + + list_for_each_entry(node, &lcd_fb_list, list) { + if (props->match(props, node->fb_info)) { + /* node->fb->lcd_props = props + * get_device(props->lcd_deb) */ + } + } + spin_unlock(&lcd_list_lock); + + printk(KERN_INFO "Registered lcd device: name=%s\n", new_lcd->class_dev.class_id); + + return 0; +} +EXPORT_SYMBOL(lcd_device_register); + +/** + * lcd_device_unregister - unregisters a object of lcd_properties class. + * @props: the property to unreigister + * + * Unregisters a previously registered via lcd_device_register object. + */ +void lcd_device_unregister(struct lcd_properties *props) +{ + struct lcd_class_device *lcd_dev; + if (!props) + return; + + lcd_dev = props->lcd_class_dev; + + pr_debug("lcd_device_unregister: name=%s\n", lcd_dev->class_dev.class_id); + + class_device_remove_file (&lcd_dev->class_dev, &class_device_attr_contrast); + class_device_remove_file (&lcd_dev->class_dev, &class_device_attr_max_contrast); + class_device_remove_file (&lcd_dev->class_dev, &class_device_attr_power); + + spin_lock(&lcd_dev->lock); + lcd_dev->props = NULL; + props->lcd_class_dev = NULL; + spin_unlock(&lcd_dev->lock); + + /* remove from device list */ + spin_lock(&lcd_list_lock); + list_del(&lcd_dev->list); + spin_unlock(&lcd_list_lock); + + class_device_unregister(&lcd_dev->class_dev); +} +EXPORT_SYMBOL(lcd_device_unregister); + +int lcd_fb_info_register(struct fb_info *fb) +{ + struct lcd_fb_list_node *node; + struct lcd_class_device *lcd_dev; + + node = kmalloc(sizeof(struct lcd_fb_list_node), GFP_KERNEL); + if (!node) { + return -ENOMEM; + } + + node->fb_info = fb; + + /* add to fb list, and attempt to match with known lcd devices */ + spin_lock(&lcd_list_lock); + list_add_tail(&lcd_fb_list, &node->list); + + list_for_each_entry(lcd_dev, &lcd_device_list, list) { + spin_lock(&lcd_dev->lock); + if (likely(lcd_dev->props)) { + if (lcd_dev->props->match(lcd_dev->props, fb)) { + /* fb->lcd_props = lcd_dev->props; + * get_device(lcd_dev->props->lcd_dev); */ + } + } + spin_unlock(&lcd_dev->lock); + } + spin_unlock(&lcd_list_lock); + + return 0; +} + +void lcd_fb_info_unregister(struct fb_info *fb) +{ + struct lcd_fb_list_node *node; + struct lcd_fb_list_node *tmp; + + /* remove from fb list */ + spin_lock(&lcd_list_lock); + list_for_each_entry_safe(node, tmp, &lcd_fb_list, list) { + if (node->fb_info == fb) { + list_del(&node->list); + kfree(node); + } + } + + spin_unlock(&lcd_list_lock); +} + +static int __init lcd_init(void) +{ + class_register(&lcd_class); + return 0; +} +subsys_initcall(lcd_init); + +static void __exit lcd_exit(void) +{ + class_unregister(&lcd_class); +} +module_exit(lcd_exit); + +MODULE_AUTHOR("John Lenz "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LCD class interface"); +