]>
Commit | Line | Data |
---|---|---|
2ee12163 JS |
1 | /* |
2 | * display-sysfs.c - Display output driver sysfs interface | |
3 | * | |
4 | * Copyright (C) 2007 James Simmons <[email protected]> | |
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> | |
8b485877 | 29 | #include <linux/kdev_t.h> |
5a0e3ad6 | 30 | #include <linux/slab.h> |
2ee12163 JS |
31 | |
32 | static 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 | ||
39 | static 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 | ||
46 | static 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 | ||
59 | static 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 | ||
e7d2860b | 71 | if (isspace(*endp)) |
2ee12163 JS |
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 | ||
87 | static 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 | ||
101 | static 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 | ||
108 | static 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 | ||
119 | static 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 | ||
130 | static struct mutex allocated_dsp_lock; | |
131 | static DEFINE_IDR(allocated_dsp); | |
3b769be9 | 132 | static struct class *display_class; |
2ee12163 JS |
133 | |
134 | struct 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) { | |
77997aaa GKH |
157 | new_dev->dev = device_create(display_class, parent, |
158 | MKDEV(0, 0), new_dev, | |
159 | "display%d", new_dev->idx); | |
2ee12163 | 160 | if (!IS_ERR(new_dev->dev)) { |
2ee12163 JS |
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 | } | |
175 | EXPORT_SYMBOL(display_device_register); | |
176 | ||
177 | void 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); | |
25985edc | 185 | // Mark device index as available |
2ee12163 JS |
186 | mutex_lock(&allocated_dsp_lock); |
187 | idr_remove(&allocated_dsp, ddev->idx); | |
188 | mutex_unlock(&allocated_dsp_lock); | |
189 | kfree(ddev); | |
190 | } | |
191 | EXPORT_SYMBOL(display_device_unregister); | |
192 | ||
193 | static 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 | ||
208 | static void __exit display_class_exit(void) | |
209 | { | |
210 | class_destroy(display_class); | |
211 | } | |
212 | ||
213 | module_init(display_class_init); | |
214 | module_exit(display_class_exit); | |
215 | ||
216 | MODULE_DESCRIPTION("Display Hardware handling"); | |
217 | MODULE_AUTHOR("James Simmons <[email protected]>"); | |
218 | MODULE_LICENSE("GPL"); | |
219 |