/* * display-sysfs.c - Display output driver sysfs interface * * Copyright (C) 2007 James Simmons * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include static ssize_t display_show_name(struct device *dev, struct device_attribute *attr, char *buf) { struct display_device *dsp = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", dsp->name); } static ssize_t display_show_type(struct device *dev, struct device_attribute *attr, char *buf) { struct display_device *dsp = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE, "%s\n", dsp->type); } static ssize_t display_show_contrast(struct device *dev, struct device_attribute *attr, char *buf) { struct display_device *dsp = dev_get_drvdata(dev); ssize_t rc = -ENXIO; mutex_lock(&dsp->lock); if (likely(dsp->driver) && dsp->driver->get_contrast) rc = sprintf(buf, "%d\n", dsp->driver->get_contrast(dsp)); mutex_unlock(&dsp->lock); return rc; } static ssize_t display_store_contrast(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct display_device *dsp = dev_get_drvdata(dev); ssize_t ret = -EINVAL, size; int contrast; char *endp; contrast = simple_strtoul(buf, &endp, 0); size = endp - buf; if (isspace(*endp)) size++; if (size != count) return ret; mutex_lock(&dsp->lock); if (likely(dsp->driver && dsp->driver->set_contrast)) { pr_debug("display: set contrast to %d\n", contrast); dsp->driver->set_contrast(dsp, contrast); ret = count; } mutex_unlock(&dsp->lock); return ret; } static ssize_t display_show_max_contrast(struct device *dev, struct device_attribute *attr, char *buf) { struct display_device *dsp = dev_get_drvdata(dev); ssize_t rc = -ENXIO; mutex_lock(&dsp->lock); if (likely(dsp->driver)) rc = sprintf(buf, "%d\n", dsp->driver->max_contrast); mutex_unlock(&dsp->lock); return rc; } static struct device_attribute display_attrs[] = { __ATTR(name, S_IRUGO, display_show_name, NULL), __ATTR(type, S_IRUGO, display_show_type, NULL), __ATTR(contrast, S_IRUGO | S_IWUSR, display_show_contrast, display_store_contrast), __ATTR(max_contrast, S_IRUGO, display_show_max_contrast, NULL), }; static int display_suspend(struct device *dev, pm_message_t state) { struct display_device *dsp = dev_get_drvdata(dev); mutex_lock(&dsp->lock); if (likely(dsp->driver->suspend)) dsp->driver->suspend(dsp, state); mutex_unlock(&dsp->lock); return 0; }; static int display_resume(struct device *dev) { struct display_device *dsp = dev_get_drvdata(dev); mutex_lock(&dsp->lock); if (likely(dsp->driver->resume)) dsp->driver->resume(dsp); mutex_unlock(&dsp->lock); return 0; }; static struct mutex allocated_dsp_lock; static DEFINE_IDR(allocated_dsp); static struct class *display_class; struct display_device *display_device_register(struct display_driver *driver, struct device *parent, void *devdata) { struct display_device *new_dev = NULL; int ret = -EINVAL; if (unlikely(!driver)) return ERR_PTR(ret); mutex_lock(&allocated_dsp_lock); ret = idr_pre_get(&allocated_dsp, GFP_KERNEL); mutex_unlock(&allocated_dsp_lock); if (!ret) return ERR_PTR(ret); new_dev = kzalloc(sizeof(struct display_device), GFP_KERNEL); if (likely(new_dev) && unlikely(driver->probe(new_dev, devdata))) { // Reserve the index for this display mutex_lock(&allocated_dsp_lock); ret = idr_get_new(&allocated_dsp, new_dev, &new_dev->idx); mutex_unlock(&allocated_dsp_lock); if (!ret) { new_dev->dev = device_create(display_class, parent, MKDEV(0, 0), new_dev, "display%d", new_dev->idx); if (!IS_ERR(new_dev->dev)) { new_dev->parent = parent; new_dev->driver = driver; mutex_init(&new_dev->lock); return new_dev; } mutex_lock(&allocated_dsp_lock); idr_remove(&allocated_dsp, new_dev->idx); mutex_unlock(&allocated_dsp_lock); ret = -EINVAL; } } kfree(new_dev); return ERR_PTR(ret); } EXPORT_SYMBOL(display_device_register); void display_device_unregister(struct display_device *ddev) { if (!ddev) return; // Free device mutex_lock(&ddev->lock); device_unregister(ddev->dev); mutex_unlock(&ddev->lock); // Mark device index as available mutex_lock(&allocated_dsp_lock); idr_remove(&allocated_dsp, ddev->idx); mutex_unlock(&allocated_dsp_lock); kfree(ddev); } EXPORT_SYMBOL(display_device_unregister); static int __init display_class_init(void) { display_class = class_create(THIS_MODULE, "display"); if (IS_ERR(display_class)) { printk(KERN_ERR "Failed to create display class\n"); display_class = NULL; return -EINVAL; } display_class->dev_attrs = display_attrs; display_class->suspend = display_suspend; display_class->resume = display_resume; mutex_init(&allocated_dsp_lock); return 0; } static void __exit display_class_exit(void) { class_destroy(display_class); } module_init(display_class_init); module_exit(display_class_exit); MODULE_DESCRIPTION("Display Hardware handling"); MODULE_AUTHOR("James Simmons "); MODULE_LICENSE("GPL");