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