xref: /linux/drivers/video/backlight/lcd.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * LCD Lowlevel Control Abstraction
4  *
5  * Copyright (C) 2003,2004 Hewlett-Packard Company
6  *
7  */
8 
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/device.h>
14 #include <linux/lcd.h>
15 #include <linux/notifier.h>
16 #include <linux/ctype.h>
17 #include <linux/err.h>
18 #include <linux/slab.h>
19 
20 static DEFINE_MUTEX(lcd_dev_list_mutex);
21 static LIST_HEAD(lcd_dev_list);
22 
lcd_notify_blank(struct lcd_device * ld,struct device * display_dev,int power)23 static void lcd_notify_blank(struct lcd_device *ld, struct device *display_dev,
24 			     int power)
25 {
26 	guard(mutex)(&ld->ops_lock);
27 
28 	if (!ld->ops || !ld->ops->set_power)
29 		return;
30 	if (ld->ops->controls_device && !ld->ops->controls_device(ld, display_dev))
31 		return;
32 
33 	ld->ops->set_power(ld, power);
34 }
35 
lcd_notify_blank_all(struct device * display_dev,int power)36 void lcd_notify_blank_all(struct device *display_dev, int power)
37 {
38 	struct lcd_device *ld;
39 
40 	guard(mutex)(&lcd_dev_list_mutex);
41 
42 	list_for_each_entry(ld, &lcd_dev_list, entry)
43 		lcd_notify_blank(ld, display_dev, power);
44 }
45 EXPORT_SYMBOL(lcd_notify_blank_all);
46 
lcd_notify_mode_change(struct lcd_device * ld,struct device * display_dev,unsigned int width,unsigned int height)47 static void lcd_notify_mode_change(struct lcd_device *ld, struct device *display_dev,
48 				   unsigned int width, unsigned int height)
49 {
50 	guard(mutex)(&ld->ops_lock);
51 
52 	if (!ld->ops || !ld->ops->set_mode)
53 		return;
54 	if (ld->ops->controls_device && !ld->ops->controls_device(ld, display_dev))
55 		return;
56 
57 	ld->ops->set_mode(ld, width, height);
58 }
59 
lcd_notify_mode_change_all(struct device * display_dev,unsigned int width,unsigned int height)60 void lcd_notify_mode_change_all(struct device *display_dev,
61 				unsigned int width, unsigned int height)
62 {
63 	struct lcd_device *ld;
64 
65 	guard(mutex)(&lcd_dev_list_mutex);
66 
67 	list_for_each_entry(ld, &lcd_dev_list, entry)
68 		lcd_notify_mode_change(ld, display_dev, width, height);
69 }
70 EXPORT_SYMBOL(lcd_notify_mode_change_all);
71 
lcd_power_show(struct device * dev,struct device_attribute * attr,char * buf)72 static ssize_t lcd_power_show(struct device *dev, struct device_attribute *attr,
73 		char *buf)
74 {
75 	int rc;
76 	struct lcd_device *ld = to_lcd_device(dev);
77 
78 	mutex_lock(&ld->ops_lock);
79 	if (ld->ops && ld->ops->get_power)
80 		rc = sprintf(buf, "%d\n", ld->ops->get_power(ld));
81 	else
82 		rc = -ENXIO;
83 	mutex_unlock(&ld->ops_lock);
84 
85 	return rc;
86 }
87 
lcd_power_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)88 static ssize_t lcd_power_store(struct device *dev,
89 		struct device_attribute *attr, const char *buf, size_t count)
90 {
91 	int rc;
92 	struct lcd_device *ld = to_lcd_device(dev);
93 	unsigned long power;
94 
95 	rc = kstrtoul(buf, 0, &power);
96 	if (rc)
97 		return rc;
98 
99 	rc = -ENXIO;
100 
101 	mutex_lock(&ld->ops_lock);
102 	if (ld->ops && ld->ops->set_power) {
103 		pr_debug("set power to %lu\n", power);
104 		ld->ops->set_power(ld, power);
105 		rc = count;
106 	}
107 	mutex_unlock(&ld->ops_lock);
108 
109 	return rc;
110 }
111 static DEVICE_ATTR_RW(lcd_power);
112 
contrast_show(struct device * dev,struct device_attribute * attr,char * buf)113 static ssize_t contrast_show(struct device *dev,
114 		struct device_attribute *attr, char *buf)
115 {
116 	int rc = -ENXIO;
117 	struct lcd_device *ld = to_lcd_device(dev);
118 
119 	mutex_lock(&ld->ops_lock);
120 	if (ld->ops && ld->ops->get_contrast)
121 		rc = sprintf(buf, "%d\n", ld->ops->get_contrast(ld));
122 	mutex_unlock(&ld->ops_lock);
123 
124 	return rc;
125 }
126 
contrast_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)127 static ssize_t contrast_store(struct device *dev,
128 		struct device_attribute *attr, const char *buf, size_t count)
129 {
130 	int rc;
131 	struct lcd_device *ld = to_lcd_device(dev);
132 	unsigned long contrast;
133 
134 	rc = kstrtoul(buf, 0, &contrast);
135 	if (rc)
136 		return rc;
137 
138 	rc = -ENXIO;
139 
140 	mutex_lock(&ld->ops_lock);
141 	if (ld->ops && ld->ops->set_contrast) {
142 		pr_debug("set contrast to %lu\n", contrast);
143 		ld->ops->set_contrast(ld, contrast);
144 		rc = count;
145 	}
146 	mutex_unlock(&ld->ops_lock);
147 
148 	return rc;
149 }
150 static DEVICE_ATTR_RW(contrast);
151 
max_contrast_show(struct device * dev,struct device_attribute * attr,char * buf)152 static ssize_t max_contrast_show(struct device *dev,
153 		struct device_attribute *attr, char *buf)
154 {
155 	struct lcd_device *ld = to_lcd_device(dev);
156 
157 	return sprintf(buf, "%d\n", ld->props.max_contrast);
158 }
159 static DEVICE_ATTR_RO(max_contrast);
160 
lcd_device_release(struct device * dev)161 static void lcd_device_release(struct device *dev)
162 {
163 	struct lcd_device *ld = to_lcd_device(dev);
164 	kfree(ld);
165 }
166 
167 static struct attribute *lcd_device_attrs[] = {
168 	&dev_attr_lcd_power.attr,
169 	&dev_attr_contrast.attr,
170 	&dev_attr_max_contrast.attr,
171 	NULL,
172 };
173 ATTRIBUTE_GROUPS(lcd_device);
174 
175 static const struct class lcd_class = {
176 	.name = "lcd",
177 	.dev_groups = lcd_device_groups,
178 };
179 
180 /**
181  * lcd_device_register - register a new object of lcd_device class.
182  * @name: the name of the new object(must be the same as the name of the
183  *   respective framebuffer device).
184  * @parent: pointer to the parent's struct device .
185  * @devdata: an optional pointer to be stored in the device. The
186  *   methods may retrieve it by using lcd_get_data(ld).
187  * @ops: the lcd operations structure.
188  *
189  * Creates and registers a new lcd device. Returns either an ERR_PTR()
190  * or a pointer to the newly allocated device.
191  */
lcd_device_register(const char * name,struct device * parent,void * devdata,const struct lcd_ops * ops)192 struct lcd_device *lcd_device_register(const char *name, struct device *parent,
193 		void *devdata, const struct lcd_ops *ops)
194 {
195 	struct lcd_device *new_ld;
196 	int rc;
197 
198 	pr_debug("lcd_device_register: name=%s\n", name);
199 
200 	new_ld = kzalloc(sizeof(struct lcd_device), GFP_KERNEL);
201 	if (!new_ld)
202 		return ERR_PTR(-ENOMEM);
203 
204 	mutex_init(&new_ld->ops_lock);
205 	mutex_init(&new_ld->update_lock);
206 
207 	new_ld->dev.class = &lcd_class;
208 	new_ld->dev.parent = parent;
209 	new_ld->dev.release = lcd_device_release;
210 	dev_set_name(&new_ld->dev, "%s", name);
211 	dev_set_drvdata(&new_ld->dev, devdata);
212 
213 	new_ld->ops = ops;
214 
215 	rc = device_register(&new_ld->dev);
216 	if (rc) {
217 		put_device(&new_ld->dev);
218 		return ERR_PTR(rc);
219 	}
220 
221 	guard(mutex)(&lcd_dev_list_mutex);
222 	list_add(&new_ld->entry, &lcd_dev_list);
223 
224 	return new_ld;
225 }
226 EXPORT_SYMBOL(lcd_device_register);
227 
228 /**
229  * lcd_device_unregister - unregisters a object of lcd_device class.
230  * @ld: the lcd device object to be unregistered and freed.
231  *
232  * Unregisters a previously registered via lcd_device_register object.
233  */
lcd_device_unregister(struct lcd_device * ld)234 void lcd_device_unregister(struct lcd_device *ld)
235 {
236 	if (!ld)
237 		return;
238 
239 	guard(mutex)(&lcd_dev_list_mutex);
240 	list_del(&ld->entry);
241 
242 	mutex_lock(&ld->ops_lock);
243 	ld->ops = NULL;
244 	mutex_unlock(&ld->ops_lock);
245 
246 	device_unregister(&ld->dev);
247 }
248 EXPORT_SYMBOL(lcd_device_unregister);
249 
devm_lcd_device_release(struct device * dev,void * res)250 static void devm_lcd_device_release(struct device *dev, void *res)
251 {
252 	struct lcd_device *lcd = *(struct lcd_device **)res;
253 
254 	lcd_device_unregister(lcd);
255 }
256 
devm_lcd_device_match(struct device * dev,void * res,void * data)257 static int devm_lcd_device_match(struct device *dev, void *res, void *data)
258 {
259 	struct lcd_device **r = res;
260 
261 	return *r == data;
262 }
263 
264 /**
265  * devm_lcd_device_register - resource managed lcd_device_register()
266  * @dev: the device to register
267  * @name: the name of the device
268  * @parent: a pointer to the parent device
269  * @devdata: an optional pointer to be stored for private driver use
270  * @ops: the lcd operations structure
271  *
272  * @return a struct lcd on success, or an ERR_PTR on error
273  *
274  * Managed lcd_device_register(). The lcd_device returned from this function
275  * are automatically freed on driver detach. See lcd_device_register()
276  * for more information.
277  */
devm_lcd_device_register(struct device * dev,const char * name,struct device * parent,void * devdata,const struct lcd_ops * ops)278 struct lcd_device *devm_lcd_device_register(struct device *dev,
279 		const char *name, struct device *parent,
280 		void *devdata, const struct lcd_ops *ops)
281 {
282 	struct lcd_device **ptr, *lcd;
283 
284 	ptr = devres_alloc(devm_lcd_device_release, sizeof(*ptr), GFP_KERNEL);
285 	if (!ptr)
286 		return ERR_PTR(-ENOMEM);
287 
288 	lcd = lcd_device_register(name, parent, devdata, ops);
289 	if (!IS_ERR(lcd)) {
290 		*ptr = lcd;
291 		devres_add(dev, ptr);
292 	} else {
293 		devres_free(ptr);
294 	}
295 
296 	return lcd;
297 }
298 EXPORT_SYMBOL(devm_lcd_device_register);
299 
300 /**
301  * devm_lcd_device_unregister - resource managed lcd_device_unregister()
302  * @dev: the device to unregister
303  * @ld: the lcd device to unregister
304  *
305  * Deallocated a lcd allocated with devm_lcd_device_register(). Normally
306  * this function will not need to be called and the resource management
307  * code will ensure that the resource is freed.
308  */
devm_lcd_device_unregister(struct device * dev,struct lcd_device * ld)309 void devm_lcd_device_unregister(struct device *dev, struct lcd_device *ld)
310 {
311 	int rc;
312 
313 	rc = devres_release(dev, devm_lcd_device_release,
314 				devm_lcd_device_match, ld);
315 	WARN_ON(rc);
316 }
317 EXPORT_SYMBOL(devm_lcd_device_unregister);
318 
319 
lcd_class_exit(void)320 static void __exit lcd_class_exit(void)
321 {
322 	class_unregister(&lcd_class);
323 }
324 
lcd_class_init(void)325 static int __init lcd_class_init(void)
326 {
327 	int ret;
328 
329 	ret = class_register(&lcd_class);
330 	if (ret) {
331 		pr_warn("Unable to create backlight class; errno = %d\n", ret);
332 		return ret;
333 	}
334 
335 	return 0;
336 }
337 
338 /*
339  * if this is compiled into the kernel, we need to ensure that the
340  * class is registered before users of the class try to register lcd's
341  */
342 postcore_initcall(lcd_class_init);
343 module_exit(lcd_class_exit);
344 
345 MODULE_LICENSE("GPL");
346 MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
347 MODULE_DESCRIPTION("LCD Lowlevel Control Abstraction");
348