xref: /linux/drivers/video/backlight/lcd.c (revision 5e8d780d745c1619aba81fe7166c5a4b5cad2b84)
1 /*
2  * LCD Lowlevel Control Abstraction
3  *
4  * Copyright (C) 2003,2004 Hewlett-Packard Company
5  *
6  */
7 
8 #include <linux/module.h>
9 #include <linux/init.h>
10 #include <linux/device.h>
11 #include <linux/lcd.h>
12 #include <linux/notifier.h>
13 #include <linux/ctype.h>
14 #include <linux/err.h>
15 #include <linux/fb.h>
16 
17 static ssize_t lcd_show_power(struct class_device *cdev, char *buf)
18 {
19 	int rc;
20 	struct lcd_device *ld = to_lcd_device(cdev);
21 
22 	down(&ld->sem);
23 	if (likely(ld->props && ld->props->get_power))
24 		rc = sprintf(buf, "%d\n", ld->props->get_power(ld));
25 	else
26 		rc = -ENXIO;
27 	up(&ld->sem);
28 
29 	return rc;
30 }
31 
32 static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_t count)
33 {
34 	int rc = -ENXIO;
35 	char *endp;
36 	struct lcd_device *ld = to_lcd_device(cdev);
37 	int power = simple_strtoul(buf, &endp, 0);
38 	size_t size = endp - buf;
39 
40 	if (*endp && isspace(*endp))
41 		size++;
42 	if (size != count)
43 		return -EINVAL;
44 
45 	down(&ld->sem);
46 	if (likely(ld->props && ld->props->set_power)) {
47 		pr_debug("lcd: set power to %d\n", power);
48 		ld->props->set_power(ld, power);
49 		rc = count;
50 	}
51 	up(&ld->sem);
52 
53 	return rc;
54 }
55 
56 static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf)
57 {
58 	int rc = -ENXIO;
59 	struct lcd_device *ld = to_lcd_device(cdev);
60 
61 	down(&ld->sem);
62 	if (likely(ld->props && ld->props->get_contrast))
63 		rc = sprintf(buf, "%d\n", ld->props->get_contrast(ld));
64 	up(&ld->sem);
65 
66 	return rc;
67 }
68 
69 static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, size_t count)
70 {
71 	int rc = -ENXIO;
72 	char *endp;
73 	struct lcd_device *ld = to_lcd_device(cdev);
74 	int contrast = simple_strtoul(buf, &endp, 0);
75 	size_t size = endp - buf;
76 
77 	if (*endp && isspace(*endp))
78 		size++;
79 	if (size != count)
80 		return -EINVAL;
81 
82 	down(&ld->sem);
83 	if (likely(ld->props && ld->props->set_contrast)) {
84 		pr_debug("lcd: set contrast to %d\n", contrast);
85 		ld->props->set_contrast(ld, contrast);
86 		rc = count;
87 	}
88 	up(&ld->sem);
89 
90 	return rc;
91 }
92 
93 static ssize_t lcd_show_max_contrast(struct class_device *cdev, char *buf)
94 {
95 	int rc = -ENXIO;
96 	struct lcd_device *ld = to_lcd_device(cdev);
97 
98 	down(&ld->sem);
99 	if (likely(ld->props))
100 		rc = sprintf(buf, "%d\n", ld->props->max_contrast);
101 	up(&ld->sem);
102 
103 	return rc;
104 }
105 
106 static void lcd_class_release(struct class_device *dev)
107 {
108 	struct lcd_device *ld = to_lcd_device(dev);
109 	kfree(ld);
110 }
111 
112 static struct class lcd_class = {
113 	.name = "lcd",
114 	.release = lcd_class_release,
115 };
116 
117 #define DECLARE_ATTR(_name,_mode,_show,_store)			\
118 {							 	\
119 	.attr	= { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE },	\
120 	.show	= _show,					\
121 	.store	= _store,					\
122 }
123 
124 static struct class_device_attribute lcd_class_device_attributes[] = {
125 	DECLARE_ATTR(power, 0644, lcd_show_power, lcd_store_power),
126 	DECLARE_ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast),
127 	DECLARE_ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL),
128 };
129 
130 /* This callback gets called when something important happens inside a
131  * framebuffer driver. We're looking if that important event is blanking,
132  * and if it is, we're switching lcd power as well ...
133  */
134 static int fb_notifier_callback(struct notifier_block *self,
135 				 unsigned long event, void *data)
136 {
137 	struct lcd_device *ld;
138 	struct fb_event *evdata =(struct fb_event *)data;
139 
140 	/* If we aren't interested in this event, skip it immediately ... */
141 	if (event != FB_EVENT_BLANK)
142 		return 0;
143 
144 	ld = container_of(self, struct lcd_device, fb_notif);
145 	down(&ld->sem);
146 	if (ld->props)
147 		if (!ld->props->check_fb || ld->props->check_fb(evdata->info))
148 			ld->props->set_power(ld, *(int *)evdata->data);
149 	up(&ld->sem);
150 	return 0;
151 }
152 
153 /**
154  * lcd_device_register - register a new object of lcd_device class.
155  * @name: the name of the new object(must be the same as the name of the
156  *   respective framebuffer device).
157  * @devdata: an optional pointer to be stored in the class_device. The
158  *   methods may retrieve it by using class_get_devdata(ld->class_dev).
159  * @lp: the lcd properties structure.
160  *
161  * Creates and registers a new lcd class_device. Returns either an ERR_PTR()
162  * or a pointer to the newly allocated device.
163  */
164 struct lcd_device *lcd_device_register(const char *name, void *devdata,
165 				       struct lcd_properties *lp)
166 {
167 	int i, rc;
168 	struct lcd_device *new_ld;
169 
170 	pr_debug("lcd_device_register: name=%s\n", name);
171 
172 	new_ld = kmalloc(sizeof(struct lcd_device), GFP_KERNEL);
173 	if (unlikely(!new_ld))
174 		return ERR_PTR(-ENOMEM);
175 
176 	init_MUTEX(&new_ld->sem);
177 	new_ld->props = lp;
178 	memset(&new_ld->class_dev, 0, sizeof(new_ld->class_dev));
179 	new_ld->class_dev.class = &lcd_class;
180 	strlcpy(new_ld->class_dev.class_id, name, KOBJ_NAME_LEN);
181 	class_set_devdata(&new_ld->class_dev, devdata);
182 
183 	rc = class_device_register(&new_ld->class_dev);
184 	if (unlikely(rc)) {
185 error:		kfree(new_ld);
186 		return ERR_PTR(rc);
187 	}
188 
189 	memset(&new_ld->fb_notif, 0, sizeof(new_ld->fb_notif));
190 	new_ld->fb_notif.notifier_call = fb_notifier_callback;
191 
192 	rc = fb_register_client(&new_ld->fb_notif);
193 	if (unlikely(rc))
194 		goto error;
195 
196 	for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) {
197 		rc = class_device_create_file(&new_ld->class_dev,
198 					      &lcd_class_device_attributes[i]);
199 		if (unlikely(rc)) {
200 			while (--i >= 0)
201 				class_device_remove_file(&new_ld->class_dev,
202 							 &lcd_class_device_attributes[i]);
203 			class_device_unregister(&new_ld->class_dev);
204 			/* No need to kfree(new_ld) since release() method was called */
205 			return ERR_PTR(rc);
206 		}
207 	}
208 
209 	return new_ld;
210 }
211 EXPORT_SYMBOL(lcd_device_register);
212 
213 /**
214  * lcd_device_unregister - unregisters a object of lcd_device class.
215  * @ld: the lcd device object to be unregistered and freed.
216  *
217  * Unregisters a previously registered via lcd_device_register object.
218  */
219 void lcd_device_unregister(struct lcd_device *ld)
220 {
221 	int i;
222 
223 	if (!ld)
224 		return;
225 
226 	pr_debug("lcd_device_unregister: name=%s\n", ld->class_dev.class_id);
227 
228 	for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++)
229 		class_device_remove_file(&ld->class_dev,
230 					 &lcd_class_device_attributes[i]);
231 
232 	down(&ld->sem);
233 	ld->props = NULL;
234 	up(&ld->sem);
235 
236 	fb_unregister_client(&ld->fb_notif);
237 
238 	class_device_unregister(&ld->class_dev);
239 }
240 EXPORT_SYMBOL(lcd_device_unregister);
241 
242 static void __exit lcd_class_exit(void)
243 {
244 	class_unregister(&lcd_class);
245 }
246 
247 static int __init lcd_class_init(void)
248 {
249 	return class_register(&lcd_class);
250 }
251 
252 /*
253  * if this is compiled into the kernel, we need to ensure that the
254  * class is registered before users of the class try to register lcd's
255  */
256 postcore_initcall(lcd_class_init);
257 module_exit(lcd_class_exit);
258 
259 MODULE_LICENSE("GPL");
260 MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
261 MODULE_DESCRIPTION("LCD Lowlevel Control Abstraction");
262