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 30static ssize_t display_show_name(struct device *dev, 31 struct device_attribute *attr, char *buf) 32{ 33 struct display_device *dsp = dev_get_drvdata(dev); 34 return snprintf(buf, PAGE_SIZE, "%s\n", dsp->name); 35} 36 37static ssize_t display_show_type(struct device *dev, 38 struct device_attribute *attr, char *buf) 39{ 40 struct display_device *dsp = dev_get_drvdata(dev); 41 return snprintf(buf, PAGE_SIZE, "%s\n", dsp->type); 42} 43 44static ssize_t display_show_contrast(struct device *dev, 45 struct device_attribute *attr, char *buf) 46{ 47 struct display_device *dsp = dev_get_drvdata(dev); 48 ssize_t rc = -ENXIO; 49 50 mutex_lock(&dsp->lock); 51 if (likely(dsp->driver) && dsp->driver->get_contrast) 52 rc = sprintf(buf, "%d\n", dsp->driver->get_contrast(dsp)); 53 mutex_unlock(&dsp->lock); 54 return rc; 55} 56 57static ssize_t display_store_contrast(struct device *dev, 58 struct device_attribute *attr, 59 const char *buf, size_t count) 60{ 61 struct display_device *dsp = dev_get_drvdata(dev); 62 ssize_t ret = -EINVAL, size; 63 int contrast; 64 char *endp; 65 66 contrast = simple_strtoul(buf, &endp, 0); 67 size = endp - buf; 68 69 if (*endp && isspace(*endp)) 70 size++; 71 72 if (size != count) 73 return ret; 74 75 mutex_lock(&dsp->lock); 76 if (likely(dsp->driver && dsp->driver->set_contrast)) { 77 pr_debug("display: set contrast to %d\n", contrast); 78 dsp->driver->set_contrast(dsp, contrast); 79 ret = count; 80 } 81 mutex_unlock(&dsp->lock); 82 return ret; 83} 84 85static ssize_t display_show_max_contrast(struct device *dev, 86 struct device_attribute *attr, 87 char *buf) 88{ 89 struct display_device *dsp = dev_get_drvdata(dev); 90 ssize_t rc = -ENXIO; 91 92 mutex_lock(&dsp->lock); 93 if (likely(dsp->driver)) 94 rc = sprintf(buf, "%d\n", dsp->driver->max_contrast); 95 mutex_unlock(&dsp->lock); 96 return rc; 97} 98 99static struct device_attribute display_attrs[] = { 100 __ATTR(name, S_IRUGO, display_show_name, NULL), 101 __ATTR(type, S_IRUGO, display_show_type, NULL), 102 __ATTR(contrast, S_IRUGO | S_IWUSR, display_show_contrast, display_store_contrast), 103 __ATTR(max_contrast, S_IRUGO, display_show_max_contrast, NULL), 104}; 105 106static int display_suspend(struct device *dev, pm_message_t state) 107{ 108 struct display_device *dsp = dev_get_drvdata(dev); 109 110 mutex_lock(&dsp->lock); 111 if (likely(dsp->driver->suspend)) 112 dsp->driver->suspend(dsp, state); 113 mutex_unlock(&dsp->lock); 114 return 0; 115}; 116 117static int display_resume(struct device *dev) 118{ 119 struct display_device *dsp = dev_get_drvdata(dev); 120 121 mutex_lock(&dsp->lock); 122 if (likely(dsp->driver->resume)) 123 dsp->driver->resume(dsp); 124 mutex_unlock(&dsp->lock); 125 return 0; 126}; 127 128static struct mutex allocated_dsp_lock; 129static DEFINE_IDR(allocated_dsp); 130static struct class *display_class; 131 132struct display_device *display_device_register(struct display_driver *driver, 133 struct device *parent, void *devdata) 134{ 135 struct display_device *new_dev = NULL; 136 int ret = -EINVAL; 137 138 if (unlikely(!driver)) 139 return ERR_PTR(ret); 140 141 mutex_lock(&allocated_dsp_lock); 142 ret = idr_pre_get(&allocated_dsp, GFP_KERNEL); 143 mutex_unlock(&allocated_dsp_lock); 144 if (!ret) 145 return ERR_PTR(ret); 146 147 new_dev = kzalloc(sizeof(struct display_device), GFP_KERNEL); 148 if (likely(new_dev) && unlikely(driver->probe(new_dev, devdata))) { 149 // Reserve the index for this display 150 mutex_lock(&allocated_dsp_lock); 151 ret = idr_get_new(&allocated_dsp, new_dev, &new_dev->idx); 152 mutex_unlock(&allocated_dsp_lock); 153 154 if (!ret) { 155 new_dev->dev = device_create(display_class, parent, 0, 156 "display%d", new_dev->idx); 157 if (!IS_ERR(new_dev->dev)) { 158 dev_set_drvdata(new_dev->dev, new_dev); 159 new_dev->parent = parent; 160 new_dev->driver = driver; 161 mutex_init(&new_dev->lock); 162 return new_dev; 163 } 164 mutex_lock(&allocated_dsp_lock); 165 idr_remove(&allocated_dsp, new_dev->idx); 166 mutex_unlock(&allocated_dsp_lock); 167 ret = -EINVAL; 168 } 169 } 170 kfree(new_dev); 171 return ERR_PTR(ret); 172} 173EXPORT_SYMBOL(display_device_register); 174 175void display_device_unregister(struct display_device *ddev) 176{ 177 if (!ddev) 178 return; 179 // Free device 180 mutex_lock(&ddev->lock); 181 device_unregister(ddev->dev); 182 mutex_unlock(&ddev->lock); 183 // Mark device index as avaliable 184 mutex_lock(&allocated_dsp_lock); 185 idr_remove(&allocated_dsp, ddev->idx); 186 mutex_unlock(&allocated_dsp_lock); 187 kfree(ddev); 188} 189EXPORT_SYMBOL(display_device_unregister); 190 191static int __init display_class_init(void) 192{ 193 display_class = class_create(THIS_MODULE, "display"); 194 if (IS_ERR(display_class)) { 195 printk(KERN_ERR "Failed to create display class\n"); 196 display_class = NULL; 197 return -EINVAL; 198 } 199 display_class->dev_attrs = display_attrs; 200 display_class->suspend = display_suspend; 201 display_class->resume = display_resume; 202 mutex_init(&allocated_dsp_lock); 203 return 0; 204} 205 206static void __exit display_class_exit(void) 207{ 208 class_destroy(display_class); 209} 210 211module_init(display_class_init); 212module_exit(display_class_exit); 213 214MODULE_DESCRIPTION("Display Hardware handling"); 215MODULE_AUTHOR("James Simmons <jsimmons@infradead.org>"); 216MODULE_LICENSE("GPL"); 217