Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
20static DEFINE_MUTEX(lcd_dev_list_mutex);
21static LIST_HEAD(lcd_dev_list);
22
23static 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
36void 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}
45EXPORT_SYMBOL(lcd_notify_blank_all);
46
47static 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
60void 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}
70EXPORT_SYMBOL(lcd_notify_mode_change_all);
71
72static 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
88static 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}
111static DEVICE_ATTR_RW(lcd_power);
112
113static 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
127static 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}
150static DEVICE_ATTR_RW(contrast);
151
152static 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}
159static DEVICE_ATTR_RO(max_contrast);
160
161static void lcd_device_release(struct device *dev)
162{
163 struct lcd_device *ld = to_lcd_device(dev);
164 kfree(ld);
165}
166
167static 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};
173ATTRIBUTE_GROUPS(lcd_device);
174
175static 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 */
192struct 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}
226EXPORT_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 */
234void 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}
248EXPORT_SYMBOL(lcd_device_unregister);
249
250static 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
257static 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 */
278struct 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}
298EXPORT_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 */
309void 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}
317EXPORT_SYMBOL(devm_lcd_device_unregister);
318
319
320static void __exit lcd_class_exit(void)
321{
322 class_unregister(&lcd_class);
323}
324
325static 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 */
342postcore_initcall(lcd_class_init);
343module_exit(lcd_class_exit);
344
345MODULE_LICENSE("GPL");
346MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
347MODULE_DESCRIPTION("LCD Lowlevel Control Abstraction");