File Index
Files and directories
Sign in
FILE

backlight.c

File preview with syntax highlighting.

linux-6.19.14/drivers/video/backlight/backlight.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Backlight 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/backlight.h>
 15#include <linux/notifier.h>
 16#include <linux/ctype.h>
 17#include <linux/err.h>
 18#include <linux/slab.h>
 19#include <linux/of.h>
 20
 21#ifdef CONFIG_PMAC_BACKLIGHT
 22#include <asm/backlight.h>
 23#endif
 24
 25/**
 26 * DOC: overview
 27 *
 28 * The backlight core supports implementing backlight drivers.
 29 *
 30 * A backlight driver registers a driver using
 31 * devm_backlight_device_register(). The properties of the backlight
 32 * driver such as type and max_brightness must be specified.
 33 * When the core detect changes in for example brightness or power state
 34 * the update_status() operation is called. The backlight driver shall
 35 * implement this operation and use it to adjust backlight.
 36 *
 37 * Several sysfs attributes are provided by the backlight core::
 38 *
 39 * - brightness         R/W, set the requested brightness level
 40 * - actual_brightness  RO, the brightness level used by the HW
 41 * - max_brightness     RO, the maximum  brightness level supported
 42 *
 43 * See Documentation/ABI/stable/sysfs-class-backlight for the full list.
 44 *
 45 * The backlight can be adjusted using the sysfs interface, and
 46 * the backlight driver may also support adjusting backlight using
 47 * a hot-key or some other platform or firmware specific way.
 48 *
 49 * The driver must implement the get_brightness() operation if
 50 * the HW do not support all the levels that can be specified in
 51 * brightness, thus providing user-space access to the actual level
 52 * via the actual_brightness attribute.
 53 *
 54 * When the backlight changes this is reported to user-space using
 55 * an uevent connected to the actual_brightness attribute.
 56 * When brightness is set by platform specific means, for example
 57 * a hot-key to adjust backlight, the driver must notify the backlight
 58 * core that brightness has changed using backlight_force_update().
 59 *
 60 * Display drives can control the backlight device's status using
 61 * backlight_notify_blank() and backlight_notify_blank_all(). If this
 62 * results in a change in the backlight state the functions call the
 63 * update_status() operation.
 64 */
 65
 66static struct list_head backlight_dev_list;
 67static struct mutex backlight_dev_list_mutex;
 68
 69static const char *const backlight_types[] = {
 70	[BACKLIGHT_RAW] = "raw",
 71	[BACKLIGHT_PLATFORM] = "platform",
 72	[BACKLIGHT_FIRMWARE] = "firmware",
 73};
 74
 75static const char *const backlight_scale_types[] = {
 76	[BACKLIGHT_SCALE_UNKNOWN]	= "unknown",
 77	[BACKLIGHT_SCALE_LINEAR]	= "linear",
 78	[BACKLIGHT_SCALE_NON_LINEAR]	= "non-linear",
 79};
 80
 81void backlight_notify_blank(struct backlight_device *bd, struct device *display_dev,
 82			    bool fb_on, bool prev_fb_on)
 83{
 84	guard(mutex)(&bd->ops_lock);
 85
 86	if (!bd->ops)
 87		return;
 88	if (bd->ops->controls_device && !bd->ops->controls_device(bd, display_dev))
 89		return;
 90
 91	if (fb_on && (!prev_fb_on || !bd->use_count)) {
 92		if (!bd->use_count++) {
 93			bd->props.state &= ~BL_CORE_FBBLANK;
 94			backlight_update_status(bd);
 95		}
 96	} else if (!fb_on && prev_fb_on && bd->use_count) {
 97		if (!(--bd->use_count)) {
 98			bd->props.state |= BL_CORE_FBBLANK;
 99			backlight_update_status(bd);
100		}
101	}
102}
103EXPORT_SYMBOL(backlight_notify_blank);
104
105void backlight_notify_blank_all(struct device *display_dev, bool fb_on, bool prev_fb_on)
106{
107	struct backlight_device *bd;
108
109	guard(mutex)(&backlight_dev_list_mutex);
110
111	list_for_each_entry(bd, &backlight_dev_list, entry)
112		backlight_notify_blank(bd, display_dev, fb_on, prev_fb_on);
113}
114EXPORT_SYMBOL(backlight_notify_blank_all);
115
116static void backlight_generate_event(struct backlight_device *bd,
117				     enum backlight_update_reason reason)
118{
119	char *envp[2];
120
121	switch (reason) {
122	case BACKLIGHT_UPDATE_SYSFS:
123		envp[0] = "SOURCE=sysfs";
124		break;
125	case BACKLIGHT_UPDATE_HOTKEY:
126		envp[0] = "SOURCE=hotkey";
127		break;
128	default:
129		envp[0] = "SOURCE=unknown";
130		break;
131	}
132	envp[1] = NULL;
133	kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);
134	sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
135}
136
137static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr,
138		char *buf)
139{
140	struct backlight_device *bd = to_backlight_device(dev);
141
142	return sprintf(buf, "%d\n", bd->props.power);
143}
144
145static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr,
146		const char *buf, size_t count)
147{
148	int rc;
149	struct backlight_device *bd = to_backlight_device(dev);
150	unsigned long power, old_power;
151
152	rc = kstrtoul(buf, 0, &power);
153	if (rc)
154		return rc;
155
156	rc = -ENXIO;
157	mutex_lock(&bd->ops_lock);
158	if (bd->ops) {
159		pr_debug("set power to %lu\n", power);
160		if (bd->props.power != power) {
161			old_power = bd->props.power;
162			bd->props.power = power;
163			rc = backlight_update_status(bd);
164			if (rc)
165				bd->props.power = old_power;
166			else
167				rc = count;
168		} else {
169			rc = count;
170		}
171	}
172	mutex_unlock(&bd->ops_lock);
173
174	return rc;
175}
176static DEVICE_ATTR_RW(bl_power);
177
178static ssize_t brightness_show(struct device *dev,
179		struct device_attribute *attr, char *buf)
180{
181	struct backlight_device *bd = to_backlight_device(dev);
182
183	return sprintf(buf, "%d\n", bd->props.brightness);
184}
185
186int backlight_device_set_brightness(struct backlight_device *bd,
187				    unsigned long brightness)
188{
189	int rc = -ENXIO;
190
191	mutex_lock(&bd->ops_lock);
192	if (bd->ops) {
193		if (brightness > bd->props.max_brightness)
194			rc = -EINVAL;
195		else {
196			pr_debug("set brightness to %lu\n", brightness);
197			bd->props.brightness = brightness;
198			rc = backlight_update_status(bd);
199		}
200	}
201	mutex_unlock(&bd->ops_lock);
202
203	backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
204
205	return rc;
206}
207EXPORT_SYMBOL(backlight_device_set_brightness);
208
209static ssize_t brightness_store(struct device *dev,
210		struct device_attribute *attr, const char *buf, size_t count)
211{
212	int rc;
213	struct backlight_device *bd = to_backlight_device(dev);
214	unsigned long brightness;
215
216	rc = kstrtoul(buf, 0, &brightness);
217	if (rc)
218		return rc;
219
220	rc = backlight_device_set_brightness(bd, brightness);
221
222	return rc ? rc : count;
223}
224static DEVICE_ATTR_RW(brightness);
225
226static ssize_t type_show(struct device *dev, struct device_attribute *attr,
227		char *buf)
228{
229	struct backlight_device *bd = to_backlight_device(dev);
230
231	return sprintf(buf, "%s\n", backlight_types[bd->props.type]);
232}
233static DEVICE_ATTR_RO(type);
234
235static ssize_t max_brightness_show(struct device *dev,
236		struct device_attribute *attr, char *buf)
237{
238	struct backlight_device *bd = to_backlight_device(dev);
239
240	return sprintf(buf, "%d\n", bd->props.max_brightness);
241}
242static DEVICE_ATTR_RO(max_brightness);
243
244static ssize_t actual_brightness_show(struct device *dev,
245		struct device_attribute *attr, char *buf)
246{
247	int rc = -ENXIO;
248	struct backlight_device *bd = to_backlight_device(dev);
249
250	mutex_lock(&bd->ops_lock);
251	if (bd->ops && bd->ops->get_brightness) {
252		rc = bd->ops->get_brightness(bd);
253		if (rc >= 0)
254			rc = sprintf(buf, "%d\n", rc);
255	} else {
256		rc = sprintf(buf, "%d\n", bd->props.brightness);
257	}
258	mutex_unlock(&bd->ops_lock);
259
260	return rc;
261}
262static DEVICE_ATTR_RO(actual_brightness);
263
264static ssize_t scale_show(struct device *dev,
265		struct device_attribute *attr, char *buf)
266{
267	struct backlight_device *bd = to_backlight_device(dev);
268
269	if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR))
270		return sprintf(buf, "unknown\n");
271
272	return sprintf(buf, "%s\n", backlight_scale_types[bd->props.scale]);
273}
274static DEVICE_ATTR_RO(scale);
275
276#ifdef CONFIG_PM_SLEEP
277static int backlight_suspend(struct device *dev)
278{
279	struct backlight_device *bd = to_backlight_device(dev);
280
281	mutex_lock(&bd->ops_lock);
282	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
283		bd->props.state |= BL_CORE_SUSPENDED;
284		backlight_update_status(bd);
285	}
286	mutex_unlock(&bd->ops_lock);
287
288	return 0;
289}
290
291static int backlight_resume(struct device *dev)
292{
293	struct backlight_device *bd = to_backlight_device(dev);
294
295	mutex_lock(&bd->ops_lock);
296	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
297		bd->props.state &= ~BL_CORE_SUSPENDED;
298		backlight_update_status(bd);
299	}
300	mutex_unlock(&bd->ops_lock);
301
302	return 0;
303}
304#endif
305
306static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend,
307			 backlight_resume);
308
309static void bl_device_release(struct device *dev)
310{
311	struct backlight_device *bd = to_backlight_device(dev);
312	kfree(bd);
313}
314
315static struct attribute *bl_device_attrs[] = {
316	&dev_attr_bl_power.attr,
317	&dev_attr_brightness.attr,
318	&dev_attr_actual_brightness.attr,
319	&dev_attr_max_brightness.attr,
320	&dev_attr_scale.attr,
321	&dev_attr_type.attr,
322	NULL,
323};
324ATTRIBUTE_GROUPS(bl_device);
325
326static const struct class backlight_class = {
327	.name = "backlight",
328	.dev_groups = bl_device_groups,
329	.pm = &backlight_class_dev_pm_ops,
330};
331
332/**
333 * backlight_force_update - tell the backlight subsystem that hardware state
334 *   has changed
335 * @bd: the backlight device to update
336 * @reason: reason for update
337 *
338 * Updates the internal state of the backlight in response to a hardware event,
339 * and generates an uevent to notify userspace. A backlight driver shall call
340 * backlight_force_update() when the backlight is changed using, for example,
341 * a hot-key. The updated brightness is read using get_brightness() and the
342 * brightness value is reported using an uevent.
343 */
344void backlight_force_update(struct backlight_device *bd,
345			    enum backlight_update_reason reason)
346{
347	int brightness;
348
349	mutex_lock(&bd->ops_lock);
350	if (bd->ops && bd->ops->get_brightness) {
351		brightness = bd->ops->get_brightness(bd);
352		if (brightness >= 0)
353			bd->props.brightness = brightness;
354		else
355			dev_err(&bd->dev,
356				"Could not update brightness from device: %pe\n",
357				ERR_PTR(brightness));
358	}
359	mutex_unlock(&bd->ops_lock);
360	backlight_generate_event(bd, reason);
361}
362EXPORT_SYMBOL(backlight_force_update);
363
364/* deprecated - use devm_backlight_device_register() */
365struct backlight_device *backlight_device_register(const char *name,
366	struct device *parent, void *devdata, const struct backlight_ops *ops,
367	const struct backlight_properties *props)
368{
369	struct backlight_device *new_bd;
370	int rc;
371
372	pr_debug("backlight_device_register: name=%s\n", name);
373
374	new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
375	if (!new_bd)
376		return ERR_PTR(-ENOMEM);
377
378	mutex_init(&new_bd->update_lock);
379	mutex_init(&new_bd->ops_lock);
380
381	new_bd->dev.class = &backlight_class;
382	new_bd->dev.parent = parent;
383	new_bd->dev.release = bl_device_release;
384	dev_set_name(&new_bd->dev, "%s", name);
385	dev_set_drvdata(&new_bd->dev, devdata);
386
387	/* Set default properties */
388	if (props) {
389		memcpy(&new_bd->props, props,
390		       sizeof(struct backlight_properties));
391		if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) {
392			WARN(1, "%s: invalid backlight type", name);
393			new_bd->props.type = BACKLIGHT_RAW;
394		}
395	} else {
396		new_bd->props.type = BACKLIGHT_RAW;
397	}
398
399	rc = device_register(&new_bd->dev);
400	if (rc) {
401		put_device(&new_bd->dev);
402		return ERR_PTR(rc);
403	}
404
405	new_bd->ops = ops;
406
407#ifdef CONFIG_PMAC_BACKLIGHT
408	mutex_lock(&pmac_backlight_mutex);
409	if (!pmac_backlight)
410		pmac_backlight = new_bd;
411	mutex_unlock(&pmac_backlight_mutex);
412#endif
413
414	mutex_lock(&backlight_dev_list_mutex);
415	list_add(&new_bd->entry, &backlight_dev_list);
416	mutex_unlock(&backlight_dev_list_mutex);
417
418	return new_bd;
419}
420EXPORT_SYMBOL(backlight_device_register);
421
422/** backlight_device_get_by_type - find first backlight device of a type
423 * @type: the type of backlight device
424 *
425 * Look up the first backlight device of the specified type
426 *
427 * RETURNS:
428 *
429 * Pointer to backlight device if any was found. Otherwise NULL.
430 */
431struct backlight_device *backlight_device_get_by_type(enum backlight_type type)
432{
433	bool found = false;
434	struct backlight_device *bd;
435
436	mutex_lock(&backlight_dev_list_mutex);
437	list_for_each_entry(bd, &backlight_dev_list, entry) {
438		if (bd->props.type == type) {
439			found = true;
440			break;
441		}
442	}
443	mutex_unlock(&backlight_dev_list_mutex);
444
445	return found ? bd : NULL;
446}
447EXPORT_SYMBOL(backlight_device_get_by_type);
448
449/**
450 * backlight_device_get_by_name - Get backlight device by name
451 * @name: Device name
452 *
453 * This function looks up a backlight device by its name. It obtains a reference
454 * on the backlight device and it is the caller's responsibility to drop the
455 * reference by calling put_device().
456 *
457 * Returns:
458 * A pointer to the backlight device if found, otherwise NULL.
459 */
460struct backlight_device *backlight_device_get_by_name(const char *name)
461{
462	struct device *dev;
463
464	dev = class_find_device_by_name(&backlight_class, name);
465
466	return dev ? to_backlight_device(dev) : NULL;
467}
468EXPORT_SYMBOL(backlight_device_get_by_name);
469
470/* deprecated - use devm_backlight_device_unregister() */
471void backlight_device_unregister(struct backlight_device *bd)
472{
473	if (!bd)
474		return;
475
476	mutex_lock(&backlight_dev_list_mutex);
477	list_del(&bd->entry);
478	mutex_unlock(&backlight_dev_list_mutex);
479
480#ifdef CONFIG_PMAC_BACKLIGHT
481	mutex_lock(&pmac_backlight_mutex);
482	if (pmac_backlight == bd)
483		pmac_backlight = NULL;
484	mutex_unlock(&pmac_backlight_mutex);
485#endif
486
487	mutex_lock(&bd->ops_lock);
488	bd->ops = NULL;
489	mutex_unlock(&bd->ops_lock);
490
491	device_unregister(&bd->dev);
492}
493EXPORT_SYMBOL(backlight_device_unregister);
494
495static void devm_backlight_device_release(struct device *dev, void *res)
496{
497	struct backlight_device *backlight = *(struct backlight_device **)res;
498
499	backlight_device_unregister(backlight);
500}
501
502static int devm_backlight_device_match(struct device *dev, void *res,
503					void *data)
504{
505	struct backlight_device **r = res;
506
507	return *r == data;
508}
509
510/**
511 * devm_backlight_device_register - register a new backlight device
512 * @dev: the device to register
513 * @name: the name of the device
514 * @parent: a pointer to the parent device (often the same as @dev)
515 * @devdata: an optional pointer to be stored for private driver use
516 * @ops: the backlight operations structure
517 * @props: the backlight properties
518 *
519 * Creates and registers new backlight device. When a backlight device
520 * is registered the configuration must be specified in the @props
521 * parameter. See description of &backlight_properties.
522 *
523 * RETURNS:
524 *
525 * struct backlight on success, or an ERR_PTR on error
526 */
527struct backlight_device *devm_backlight_device_register(struct device *dev,
528	const char *name, struct device *parent, void *devdata,
529	const struct backlight_ops *ops,
530	const struct backlight_properties *props)
531{
532	struct backlight_device **ptr, *backlight;
533
534	ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr),
535			GFP_KERNEL);
536	if (!ptr)
537		return ERR_PTR(-ENOMEM);
538
539	backlight = backlight_device_register(name, parent, devdata, ops,
540						props);
541	if (!IS_ERR(backlight)) {
542		*ptr = backlight;
543		devres_add(dev, ptr);
544	} else {
545		devres_free(ptr);
546	}
547
548	return backlight;
549}
550EXPORT_SYMBOL(devm_backlight_device_register);
551
552/**
553 * devm_backlight_device_unregister - unregister backlight device
554 * @dev: the device to unregister
555 * @bd: the backlight device to unregister
556 *
557 * Deallocates a backlight allocated with devm_backlight_device_register().
558 * Normally this function will not need to be called and the resource management
559 * code will ensure that the resources are freed.
560 */
561void devm_backlight_device_unregister(struct device *dev,
562				struct backlight_device *bd)
563{
564	int rc;
565
566	rc = devres_release(dev, devm_backlight_device_release,
567				devm_backlight_device_match, bd);
568	WARN_ON(rc);
569}
570EXPORT_SYMBOL(devm_backlight_device_unregister);
571
572#ifdef CONFIG_OF
573static int of_parent_match(struct device *dev, const void *data)
574{
575	return dev->parent && dev->parent->of_node == data;
576}
577
578/**
579 * of_find_backlight_by_node() - find backlight device by device-tree node
580 * @node: device-tree node of the backlight device
581 *
582 * Returns a pointer to the backlight device corresponding to the given DT
583 * node or NULL if no such backlight device exists or if the device hasn't
584 * been probed yet.
585 *
586 * This function obtains a reference on the backlight device and it is the
587 * caller's responsibility to drop the reference by calling put_device() on
588 * the backlight device's .dev field.
589 */
590struct backlight_device *of_find_backlight_by_node(struct device_node *node)
591{
592	struct device *dev;
593
594	dev = class_find_device(&backlight_class, NULL, node, of_parent_match);
595
596	return dev ? to_backlight_device(dev) : NULL;
597}
598EXPORT_SYMBOL(of_find_backlight_by_node);
599#endif
600
601static struct backlight_device *of_find_backlight(struct device *dev)
602{
603	struct backlight_device *bd = NULL;
604	struct device_node *np;
605
606	if (!dev)
607		return NULL;
608
609	if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
610		np = of_parse_phandle(dev->of_node, "backlight", 0);
611		if (np) {
612			bd = of_find_backlight_by_node(np);
613			of_node_put(np);
614			if (!bd)
615				return ERR_PTR(-EPROBE_DEFER);
616		}
617	}
618
619	return bd;
620}
621
622static void devm_backlight_release(void *data)
623{
624	struct backlight_device *bd = data;
625
626	put_device(&bd->dev);
627}
628
629/**
630 * devm_of_find_backlight - find backlight for a device
631 * @dev: the device
632 *
633 * This function looks for a property named 'backlight' on the DT node
634 * connected to @dev and looks up the backlight device. The lookup is
635 * device managed so the reference to the backlight device is automatically
636 * dropped on driver detach.
637 *
638 * RETURNS:
639 *
640 * A pointer to the backlight device if found.
641 * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight
642 * device is found. NULL if there's no backlight property.
643 */
644struct backlight_device *devm_of_find_backlight(struct device *dev)
645{
646	struct backlight_device *bd;
647	int ret;
648
649	bd = of_find_backlight(dev);
650	if (IS_ERR_OR_NULL(bd))
651		return bd;
652	ret = devm_add_action_or_reset(dev, devm_backlight_release, bd);
653	if (ret)
654		return ERR_PTR(ret);
655
656	return bd;
657}
658EXPORT_SYMBOL(devm_of_find_backlight);
659
660static void __exit backlight_class_exit(void)
661{
662	class_unregister(&backlight_class);
663}
664
665static int __init backlight_class_init(void)
666{
667	int ret;
668
669	ret = class_register(&backlight_class);
670	if (ret) {
671		pr_warn("Unable to create backlight class; errno = %d\n", ret);
672		return ret;
673	}
674
675	INIT_LIST_HEAD(&backlight_dev_list);
676	mutex_init(&backlight_dev_list_mutex);
677
678	return 0;
679}
680
681/*
682 * if this is compiled into the kernel, we need to ensure that the
683 * class is registered before users of the class try to register lcd's
684 */
685postcore_initcall(backlight_class_init);
686module_exit(backlight_class_exit);
687
688MODULE_LICENSE("GPL");
689MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
690MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
~/ Read-only file preview.