xref: /linux/drivers/video/backlight/lcd.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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/fb.h>
19 #include <linux/slab.h>
20 
21 #if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
22 			   defined(CONFIG_LCD_CLASS_DEVICE_MODULE))
23 static int to_lcd_power(int fb_blank)
24 {
25 	switch (fb_blank) {
26 	case FB_BLANK_UNBLANK:
27 		return LCD_POWER_ON;
28 	/* deprecated; TODO: should become 'off' */
29 	case FB_BLANK_NORMAL:
30 		return LCD_POWER_REDUCED;
31 	case FB_BLANK_VSYNC_SUSPEND:
32 		return LCD_POWER_REDUCED_VSYNC_SUSPEND;
33 	/* 'off' */
34 	case FB_BLANK_HSYNC_SUSPEND:
35 	case FB_BLANK_POWERDOWN:
36 	default:
37 		return LCD_POWER_OFF;
38 	}
39 }
40 
41 /* This callback gets called when something important happens inside a
42  * framebuffer driver. We're looking if that important event is blanking,
43  * and if it is, we're switching lcd power as well ...
44  */
45 static int fb_notifier_callback(struct notifier_block *self,
46 				 unsigned long event, void *data)
47 {
48 	struct lcd_device *ld = container_of(self, struct lcd_device, fb_notif);
49 	struct fb_event *evdata = data;
50 	struct fb_info *info = evdata->info;
51 	struct lcd_device *fb_lcd = fb_lcd_device(info);
52 
53 	guard(mutex)(&ld->ops_lock);
54 
55 	if (!ld->ops)
56 		return 0;
57 	if (ld->ops->controls_device && !ld->ops->controls_device(ld, info->device))
58 		return 0;
59 	if (fb_lcd && fb_lcd != ld)
60 		return 0;
61 
62 	if (event == FB_EVENT_BLANK) {
63 		int power = to_lcd_power(*(int *)evdata->data);
64 
65 		if (ld->ops->set_power)
66 			ld->ops->set_power(ld, power);
67 	} else {
68 		const struct fb_videomode *videomode = evdata->data;
69 
70 		if (ld->ops->set_mode)
71 			ld->ops->set_mode(ld, videomode->xres, videomode->yres);
72 	}
73 
74 	return 0;
75 }
76 
77 static int lcd_register_fb(struct lcd_device *ld)
78 {
79 	memset(&ld->fb_notif, 0, sizeof(ld->fb_notif));
80 	ld->fb_notif.notifier_call = fb_notifier_callback;
81 	return fb_register_client(&ld->fb_notif);
82 }
83 
84 static void lcd_unregister_fb(struct lcd_device *ld)
85 {
86 	fb_unregister_client(&ld->fb_notif);
87 }
88 #else
89 static int lcd_register_fb(struct lcd_device *ld)
90 {
91 	return 0;
92 }
93 
94 static inline void lcd_unregister_fb(struct lcd_device *ld)
95 {
96 }
97 #endif /* CONFIG_FB */
98 
99 static ssize_t lcd_power_show(struct device *dev, struct device_attribute *attr,
100 		char *buf)
101 {
102 	int rc;
103 	struct lcd_device *ld = to_lcd_device(dev);
104 
105 	mutex_lock(&ld->ops_lock);
106 	if (ld->ops && ld->ops->get_power)
107 		rc = sprintf(buf, "%d\n", ld->ops->get_power(ld));
108 	else
109 		rc = -ENXIO;
110 	mutex_unlock(&ld->ops_lock);
111 
112 	return rc;
113 }
114 
115 static ssize_t lcd_power_store(struct device *dev,
116 		struct device_attribute *attr, const char *buf, size_t count)
117 {
118 	int rc;
119 	struct lcd_device *ld = to_lcd_device(dev);
120 	unsigned long power;
121 
122 	rc = kstrtoul(buf, 0, &power);
123 	if (rc)
124 		return rc;
125 
126 	rc = -ENXIO;
127 
128 	mutex_lock(&ld->ops_lock);
129 	if (ld->ops && ld->ops->set_power) {
130 		pr_debug("set power to %lu\n", power);
131 		ld->ops->set_power(ld, power);
132 		rc = count;
133 	}
134 	mutex_unlock(&ld->ops_lock);
135 
136 	return rc;
137 }
138 static DEVICE_ATTR_RW(lcd_power);
139 
140 static ssize_t contrast_show(struct device *dev,
141 		struct device_attribute *attr, char *buf)
142 {
143 	int rc = -ENXIO;
144 	struct lcd_device *ld = to_lcd_device(dev);
145 
146 	mutex_lock(&ld->ops_lock);
147 	if (ld->ops && ld->ops->get_contrast)
148 		rc = sprintf(buf, "%d\n", ld->ops->get_contrast(ld));
149 	mutex_unlock(&ld->ops_lock);
150 
151 	return rc;
152 }
153 
154 static ssize_t contrast_store(struct device *dev,
155 		struct device_attribute *attr, const char *buf, size_t count)
156 {
157 	int rc;
158 	struct lcd_device *ld = to_lcd_device(dev);
159 	unsigned long contrast;
160 
161 	rc = kstrtoul(buf, 0, &contrast);
162 	if (rc)
163 		return rc;
164 
165 	rc = -ENXIO;
166 
167 	mutex_lock(&ld->ops_lock);
168 	if (ld->ops && ld->ops->set_contrast) {
169 		pr_debug("set contrast to %lu\n", contrast);
170 		ld->ops->set_contrast(ld, contrast);
171 		rc = count;
172 	}
173 	mutex_unlock(&ld->ops_lock);
174 
175 	return rc;
176 }
177 static DEVICE_ATTR_RW(contrast);
178 
179 static ssize_t max_contrast_show(struct device *dev,
180 		struct device_attribute *attr, char *buf)
181 {
182 	struct lcd_device *ld = to_lcd_device(dev);
183 
184 	return sprintf(buf, "%d\n", ld->props.max_contrast);
185 }
186 static DEVICE_ATTR_RO(max_contrast);
187 
188 static void lcd_device_release(struct device *dev)
189 {
190 	struct lcd_device *ld = to_lcd_device(dev);
191 	kfree(ld);
192 }
193 
194 static struct attribute *lcd_device_attrs[] = {
195 	&dev_attr_lcd_power.attr,
196 	&dev_attr_contrast.attr,
197 	&dev_attr_max_contrast.attr,
198 	NULL,
199 };
200 ATTRIBUTE_GROUPS(lcd_device);
201 
202 static const struct class lcd_class = {
203 	.name = "lcd",
204 	.dev_groups = lcd_device_groups,
205 };
206 
207 /**
208  * lcd_device_register - register a new object of lcd_device class.
209  * @name: the name of the new object(must be the same as the name of the
210  *   respective framebuffer device).
211  * @parent: pointer to the parent's struct device .
212  * @devdata: an optional pointer to be stored in the device. The
213  *   methods may retrieve it by using lcd_get_data(ld).
214  * @ops: the lcd operations structure.
215  *
216  * Creates and registers a new lcd device. Returns either an ERR_PTR()
217  * or a pointer to the newly allocated device.
218  */
219 struct lcd_device *lcd_device_register(const char *name, struct device *parent,
220 		void *devdata, const struct lcd_ops *ops)
221 {
222 	struct lcd_device *new_ld;
223 	int rc;
224 
225 	pr_debug("lcd_device_register: name=%s\n", name);
226 
227 	new_ld = kzalloc(sizeof(struct lcd_device), GFP_KERNEL);
228 	if (!new_ld)
229 		return ERR_PTR(-ENOMEM);
230 
231 	mutex_init(&new_ld->ops_lock);
232 	mutex_init(&new_ld->update_lock);
233 
234 	new_ld->dev.class = &lcd_class;
235 	new_ld->dev.parent = parent;
236 	new_ld->dev.release = lcd_device_release;
237 	dev_set_name(&new_ld->dev, "%s", name);
238 	dev_set_drvdata(&new_ld->dev, devdata);
239 
240 	new_ld->ops = ops;
241 
242 	rc = device_register(&new_ld->dev);
243 	if (rc) {
244 		put_device(&new_ld->dev);
245 		return ERR_PTR(rc);
246 	}
247 
248 	rc = lcd_register_fb(new_ld);
249 	if (rc) {
250 		device_unregister(&new_ld->dev);
251 		return ERR_PTR(rc);
252 	}
253 
254 	return new_ld;
255 }
256 EXPORT_SYMBOL(lcd_device_register);
257 
258 /**
259  * lcd_device_unregister - unregisters a object of lcd_device class.
260  * @ld: the lcd device object to be unregistered and freed.
261  *
262  * Unregisters a previously registered via lcd_device_register object.
263  */
264 void lcd_device_unregister(struct lcd_device *ld)
265 {
266 	if (!ld)
267 		return;
268 
269 	mutex_lock(&ld->ops_lock);
270 	ld->ops = NULL;
271 	mutex_unlock(&ld->ops_lock);
272 	lcd_unregister_fb(ld);
273 
274 	device_unregister(&ld->dev);
275 }
276 EXPORT_SYMBOL(lcd_device_unregister);
277 
278 static void devm_lcd_device_release(struct device *dev, void *res)
279 {
280 	struct lcd_device *lcd = *(struct lcd_device **)res;
281 
282 	lcd_device_unregister(lcd);
283 }
284 
285 static int devm_lcd_device_match(struct device *dev, void *res, void *data)
286 {
287 	struct lcd_device **r = res;
288 
289 	return *r == data;
290 }
291 
292 /**
293  * devm_lcd_device_register - resource managed lcd_device_register()
294  * @dev: the device to register
295  * @name: the name of the device
296  * @parent: a pointer to the parent device
297  * @devdata: an optional pointer to be stored for private driver use
298  * @ops: the lcd operations structure
299  *
300  * @return a struct lcd on success, or an ERR_PTR on error
301  *
302  * Managed lcd_device_register(). The lcd_device returned from this function
303  * are automatically freed on driver detach. See lcd_device_register()
304  * for more information.
305  */
306 struct lcd_device *devm_lcd_device_register(struct device *dev,
307 		const char *name, struct device *parent,
308 		void *devdata, const struct lcd_ops *ops)
309 {
310 	struct lcd_device **ptr, *lcd;
311 
312 	ptr = devres_alloc(devm_lcd_device_release, sizeof(*ptr), GFP_KERNEL);
313 	if (!ptr)
314 		return ERR_PTR(-ENOMEM);
315 
316 	lcd = lcd_device_register(name, parent, devdata, ops);
317 	if (!IS_ERR(lcd)) {
318 		*ptr = lcd;
319 		devres_add(dev, ptr);
320 	} else {
321 		devres_free(ptr);
322 	}
323 
324 	return lcd;
325 }
326 EXPORT_SYMBOL(devm_lcd_device_register);
327 
328 /**
329  * devm_lcd_device_unregister - resource managed lcd_device_unregister()
330  * @dev: the device to unregister
331  * @ld: the lcd device to unregister
332  *
333  * Deallocated a lcd allocated with devm_lcd_device_register(). Normally
334  * this function will not need to be called and the resource management
335  * code will ensure that the resource is freed.
336  */
337 void devm_lcd_device_unregister(struct device *dev, struct lcd_device *ld)
338 {
339 	int rc;
340 
341 	rc = devres_release(dev, devm_lcd_device_release,
342 				devm_lcd_device_match, ld);
343 	WARN_ON(rc);
344 }
345 EXPORT_SYMBOL(devm_lcd_device_unregister);
346 
347 
348 static void __exit lcd_class_exit(void)
349 {
350 	class_unregister(&lcd_class);
351 }
352 
353 static int __init lcd_class_init(void)
354 {
355 	int ret;
356 
357 	ret = class_register(&lcd_class);
358 	if (ret) {
359 		pr_warn("Unable to create backlight class; errno = %d\n", ret);
360 		return ret;
361 	}
362 
363 	return 0;
364 }
365 
366 /*
367  * if this is compiled into the kernel, we need to ensure that the
368  * class is registered before users of the class try to register lcd's
369  */
370 postcore_initcall(lcd_class_init);
371 module_exit(lcd_class_exit);
372 
373 MODULE_LICENSE("GPL");
374 MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
375 MODULE_DESCRIPTION("LCD Lowlevel Control Abstraction");
376