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