1/* 2 * display-sysfs.c - Display output driver sysfs interface 3 * 4 * Copyright (C) 2007 James Simmons <jsimmons@infradead.org> 5 * 6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or (at 11 * your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 * 22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 23 */ 24#include <linux/module.h> 25#include <linux/display.h> 26#include <linux/ctype.h> 27#include <linux/idr.h> 28#include <linux/err.h> 29#include <linux/kdev_t.h> 30#include <linux/slab.h> 31 32static ssize_t display_show_name(struct device *dev, 33 struct device_attribute *attr, char *buf) 34{ 35 struct display_device *dsp = dev_get_drvdata(dev); 36 return snprintf(buf, PAGE_SIZE, "%s\n", dsp->name); 37} 38 39static ssize_t display_show_type(struct device *dev, 40 struct device_attribute *attr, char *buf) 41{ 42 struct display_device *dsp = dev_get_drvdata(dev); 43 return snprintf(buf, PAGE_SIZE, "%s\n", dsp->type); 44} 45 46static ssize_t display_show_contrast(struct device *dev, 47 struct device_attribute *attr, char *buf) 48{ 49 struct display_device *dsp = dev_get_drvdata(dev); 50 ssize_t rc = -ENXIO; 51 52 mutex_lock(&dsp->lock); 53 if (likely(dsp->driver) && dsp->driver->get_contrast) 54 rc = sprintf(buf, "%d\n", dsp->driver->get_contrast(dsp)); 55 mutex_unlock(&dsp->lock); 56 return rc; 57} 58 59static ssize_t display_store_contrast(struct device *dev, 60 struct device_attribute *attr, 61 const char *buf, size_t count) 62{ 63 struct display_device *dsp = dev_get_drvdata(dev); 64 ssize_t ret = -EINVAL, size; 65 int contrast; 66 char *endp; 67 68 contrast = simple_strtoul(buf, &endp, 0); 69 size = endp - buf; 70 71 if (isspace(*endp)) 72 size++; 73 74 if (size != count) 75 return ret; 76 77 mutex_lock(&dsp->lock); 78 if (likely(dsp->driver && dsp->driver->set_contrast)) { 79 pr_debug("display: set contrast to %d\n", contrast); 80 dsp->driver->set_contrast(dsp, contrast); 81 ret = count; 82 } 83 mutex_unlock(&dsp->lock); 84 return ret; 85} 86 87static ssize_t display_show_max_contrast(struct device *dev, 88 struct device_attribute *attr, 89 char *buf) 90{ 91 struct display_device *dsp = dev_get_drvdata(dev); 92 ssize_t rc = -ENXIO; 93 94 mutex_lock(&dsp->lock); 95 if (likely(dsp->driver)) 96 rc = sprintf(buf, "%d\n", dsp->driver->max_contrast); 97 mutex_unlock(&dsp->lock); 98 return rc; 99} 100 101static struct device_attribute display_attrs[] = { 102 __ATTR(name, S_IRUGO, display_show_name, NULL), 103 __ATTR(type, S_IRUGO, display_show_type, NULL), 104 __ATTR(contrast, S_IRUGO | S_IWUSR, display_show_contrast, display_store_contrast), 105 __ATTR(max_contrast, S_IRUGO, display_show_max_contrast, NULL), 106}; 107 108static int display_suspend(struct device *dev, pm_message_t state) 109{ 110 struct display_device *dsp = dev_get_drvdata(dev); 111 112 mutex_lock(&dsp->lock); 113 if (likely(dsp->driver->suspend)) 114 dsp->driver->suspend(dsp, state); 115 mutex_unlock(&dsp->lock); 116 return 0; 117}; 118 119static int display_resume(struct device *dev) 120{ 121 struct display_device *dsp = dev_get_drvdata(dev); 122 123 mutex_lock(&dsp->lock); 124 if (likely(dsp->driver->resume)) 125 dsp->driver->resume(dsp); 126 mutex_unlock(&dsp->lock); 127 return 0; 128}; 129 130static struct mutex allocated_dsp_lock; 131static DEFINE_IDR(allocated_dsp); 132static struct class *display_class; 133 134struct display_device *display_device_register(struct display_driver *driver, 135 struct device *parent, void *devdata) 136{ 137 struct display_device *new_dev = NULL; 138 int ret = -EINVAL; 139 140 if (unlikely(!driver)) 141 return ERR_PTR(ret); 142 143 mutex_lock(&allocated_dsp_lock); 144 ret = idr_pre_get(&allocated_dsp, GFP_KERNEL); 145 mutex_unlock(&allocated_dsp_lock); 146 if (!ret) 147 return ERR_PTR(ret); 148 149 new_dev = kzalloc(sizeof(struct display_device), GFP_KERNEL); 150 if (likely(new_dev) && unlikely(driver->probe(new_dev, devdata))) { 151 // Reserve the index for this display 152 mutex_lock(&allocated_dsp_lock); 153 ret = idr_get_new(&allocated_dsp, new_dev, &new_dev->idx); 154 mutex_unlock(&allocated_dsp_lock); 155 156 if (!ret) { 157 new_dev->dev = device_create(display_class, parent, 158 MKDEV(0, 0), new_dev, 159 "display%d", new_dev->idx); 160 if (!IS_ERR(new_dev->dev)) { 161 new_dev->parent = parent; 162 new_dev->driver = driver; 163 mutex_init(&new_dev->lock); 164 return new_dev; 165 } 166 mutex_lock(&allocated_dsp_lock); 167 idr_remove(&allocated_dsp, new_dev->idx); 168 mutex_unlock(&allocated_dsp_lock); 169 ret = -EINVAL; 170 } 171 } 172 kfree(new_dev); 173 return ERR_PTR(ret); 174} 175EXPORT_SYMBOL(display_device_register); 176 177void display_device_unregister(struct display_device *ddev) 178{ 179 if (!ddev) 180 return; 181 // Free device 182 mutex_lock(&ddev->lock); 183 device_unregister(ddev->dev); 184 mutex_unlock(&ddev->lock); 185 // Mark device index as avaliable 186 mutex_lock(&allocated_dsp_lock); 187 idr_remove(&allocated_dsp, ddev->idx); 188 mutex_unlock(&allocated_dsp_lock); 189 kfree(ddev); 190} 191EXPORT_SYMBOL(display_device_unregister); 192 193static int __init display_class_init(void) 194{ 195 display_class = class_create(THIS_MODULE, "display"); 196 if (IS_ERR(display_class)) { 197 printk(KERN_ERR "Failed to create display class\n"); 198 display_class = NULL; 199 return -EINVAL; 200 } 201 display_class->dev_attrs = display_attrs; 202 display_class->suspend = display_suspend; 203 display_class->resume = display_resume; 204 mutex_init(&allocated_dsp_lock); 205 return 0; 206} 207 208static void __exit display_class_exit(void) 209{ 210 class_destroy(display_class); 211} 212 213module_init(display_class_init); 214module_exit(display_class_exit); 215 216MODULE_DESCRIPTION("Display Hardware handling"); 217MODULE_AUTHOR("James Simmons <jsimmons@infradead.org>"); 218MODULE_LICENSE("GPL"); 219