brintos

brintos / linux-shallow public Read only

0
0
Text · 20.7 KiB · a829346 Raw
785 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Backlight Lowlevel Control Abstraction4 *5 * Copyright (C) 2003,2004 Hewlett-Packard Company6 *7 */8 9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt10 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/fb.h>19#include <linux/slab.h>20 21#ifdef CONFIG_PMAC_BACKLIGHT22#include <asm/backlight.h>23#endif24 25/**26 * DOC: overview27 *28 * The backlight core supports implementing backlight drivers.29 *30 * A backlight driver registers a driver using31 * devm_backlight_device_register(). The properties of the backlight32 * driver such as type and max_brightness must be specified.33 * When the core detect changes in for example brightness or power state34 * the update_status() operation is called. The backlight driver shall35 * 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 level40 * - actual_brightness  RO, the brightness level used by the HW41 * - max_brightness     RO, the maximum  brightness level supported42 *43 * See Documentation/ABI/stable/sysfs-class-backlight for the full list.44 *45 * The backlight can be adjusted using the sysfs interface, and46 * the backlight driver may also support adjusting backlight using47 * a hot-key or some other platform or firmware specific way.48 *49 * The driver must implement the get_brightness() operation if50 * the HW do not support all the levels that can be specified in51 * brightness, thus providing user-space access to the actual level52 * via the actual_brightness attribute.53 *54 * When the backlight changes this is reported to user-space using55 * an uevent connected to the actual_brightness attribute.56 * When brightness is set by platform specific means, for example57 * a hot-key to adjust backlight, the driver must notify the backlight58 * core that brightness has changed using backlight_force_update().59 *60 * The backlight driver core receives notifications from fbdev and61 * if the event is FB_EVENT_BLANK and if the value of blank, from the62 * FBIOBLANK ioctrl, results in a change in the backlight state the63 * update_status() operation is called.64 */65 66static struct list_head backlight_dev_list;67static struct mutex backlight_dev_list_mutex;68static struct blocking_notifier_head backlight_notifier;69 70static const char *const backlight_types[] = {71	[BACKLIGHT_RAW] = "raw",72	[BACKLIGHT_PLATFORM] = "platform",73	[BACKLIGHT_FIRMWARE] = "firmware",74};75 76static const char *const backlight_scale_types[] = {77	[BACKLIGHT_SCALE_UNKNOWN]	= "unknown",78	[BACKLIGHT_SCALE_LINEAR]	= "linear",79	[BACKLIGHT_SCALE_NON_LINEAR]	= "non-linear",80};81 82#if defined(CONFIG_FB_CORE) || (defined(CONFIG_FB_CORE_MODULE) && \83				defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))84/*85 * fb_notifier_callback86 *87 * This callback gets called when something important happens inside a88 * framebuffer driver. The backlight core only cares about FB_BLANK_UNBLANK89 * which is reported to the driver using backlight_update_status()90 * as a state change.91 *92 * There may be several fbdev's connected to the backlight device,93 * in which case they are kept track of. A state change is only reported94 * if there is a change in backlight for the specified fbdev.95 */96static int fb_notifier_callback(struct notifier_block *self,97				unsigned long event, void *data)98{99	struct backlight_device *bd;100	struct fb_event *evdata = data;101	struct fb_info *info = evdata->info;102	struct backlight_device *fb_bd = fb_bl_device(info);103	int node = info->node;104	int fb_blank = 0;105 106	/* If we aren't interested in this event, skip it immediately ... */107	if (event != FB_EVENT_BLANK)108		return 0;109 110	bd = container_of(self, struct backlight_device, fb_notif);111	mutex_lock(&bd->ops_lock);112 113	if (!bd->ops)114		goto out;115	if (bd->ops->controls_device && !bd->ops->controls_device(bd, info->device))116		goto out;117	if (fb_bd && fb_bd != bd)118		goto out;119 120	fb_blank = *(int *)evdata->data;121	if (fb_blank == FB_BLANK_UNBLANK && !bd->fb_bl_on[node]) {122		bd->fb_bl_on[node] = true;123		if (!bd->use_count++) {124			bd->props.state &= ~BL_CORE_FBBLANK;125			backlight_update_status(bd);126		}127	} else if (fb_blank != FB_BLANK_UNBLANK && bd->fb_bl_on[node]) {128		bd->fb_bl_on[node] = false;129		if (!(--bd->use_count)) {130			bd->props.state |= BL_CORE_FBBLANK;131			backlight_update_status(bd);132		}133	}134out:135	mutex_unlock(&bd->ops_lock);136	return 0;137}138 139static int backlight_register_fb(struct backlight_device *bd)140{141	memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));142	bd->fb_notif.notifier_call = fb_notifier_callback;143 144	return fb_register_client(&bd->fb_notif);145}146 147static void backlight_unregister_fb(struct backlight_device *bd)148{149	fb_unregister_client(&bd->fb_notif);150}151#else152static inline int backlight_register_fb(struct backlight_device *bd)153{154	return 0;155}156 157static inline void backlight_unregister_fb(struct backlight_device *bd)158{159}160#endif /* CONFIG_FB_CORE */161 162static void backlight_generate_event(struct backlight_device *bd,163				     enum backlight_update_reason reason)164{165	char *envp[2];166 167	switch (reason) {168	case BACKLIGHT_UPDATE_SYSFS:169		envp[0] = "SOURCE=sysfs";170		break;171	case BACKLIGHT_UPDATE_HOTKEY:172		envp[0] = "SOURCE=hotkey";173		break;174	default:175		envp[0] = "SOURCE=unknown";176		break;177	}178	envp[1] = NULL;179	kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);180	sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");181}182 183static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr,184		char *buf)185{186	struct backlight_device *bd = to_backlight_device(dev);187 188	return sprintf(buf, "%d\n", bd->props.power);189}190 191static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr,192		const char *buf, size_t count)193{194	int rc;195	struct backlight_device *bd = to_backlight_device(dev);196	unsigned long power, old_power;197 198	rc = kstrtoul(buf, 0, &power);199	if (rc)200		return rc;201 202	rc = -ENXIO;203	mutex_lock(&bd->ops_lock);204	if (bd->ops) {205		pr_debug("set power to %lu\n", power);206		if (bd->props.power != power) {207			old_power = bd->props.power;208			bd->props.power = power;209			rc = backlight_update_status(bd);210			if (rc)211				bd->props.power = old_power;212			else213				rc = count;214		} else {215			rc = count;216		}217	}218	mutex_unlock(&bd->ops_lock);219 220	return rc;221}222static DEVICE_ATTR_RW(bl_power);223 224static ssize_t brightness_show(struct device *dev,225		struct device_attribute *attr, char *buf)226{227	struct backlight_device *bd = to_backlight_device(dev);228 229	return sprintf(buf, "%d\n", bd->props.brightness);230}231 232int backlight_device_set_brightness(struct backlight_device *bd,233				    unsigned long brightness)234{235	int rc = -ENXIO;236 237	mutex_lock(&bd->ops_lock);238	if (bd->ops) {239		if (brightness > bd->props.max_brightness)240			rc = -EINVAL;241		else {242			pr_debug("set brightness to %lu\n", brightness);243			bd->props.brightness = brightness;244			rc = backlight_update_status(bd);245		}246	}247	mutex_unlock(&bd->ops_lock);248 249	backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);250 251	return rc;252}253EXPORT_SYMBOL(backlight_device_set_brightness);254 255static ssize_t brightness_store(struct device *dev,256		struct device_attribute *attr, const char *buf, size_t count)257{258	int rc;259	struct backlight_device *bd = to_backlight_device(dev);260	unsigned long brightness;261 262	rc = kstrtoul(buf, 0, &brightness);263	if (rc)264		return rc;265 266	rc = backlight_device_set_brightness(bd, brightness);267 268	return rc ? rc : count;269}270static DEVICE_ATTR_RW(brightness);271 272static ssize_t type_show(struct device *dev, struct device_attribute *attr,273		char *buf)274{275	struct backlight_device *bd = to_backlight_device(dev);276 277	return sprintf(buf, "%s\n", backlight_types[bd->props.type]);278}279static DEVICE_ATTR_RO(type);280 281static ssize_t max_brightness_show(struct device *dev,282		struct device_attribute *attr, char *buf)283{284	struct backlight_device *bd = to_backlight_device(dev);285 286	return sprintf(buf, "%d\n", bd->props.max_brightness);287}288static DEVICE_ATTR_RO(max_brightness);289 290static ssize_t actual_brightness_show(struct device *dev,291		struct device_attribute *attr, char *buf)292{293	int rc = -ENXIO;294	struct backlight_device *bd = to_backlight_device(dev);295 296	mutex_lock(&bd->ops_lock);297	if (bd->ops && bd->ops->get_brightness) {298		rc = bd->ops->get_brightness(bd);299		if (rc >= 0)300			rc = sprintf(buf, "%d\n", rc);301	} else {302		rc = sprintf(buf, "%d\n", bd->props.brightness);303	}304	mutex_unlock(&bd->ops_lock);305 306	return rc;307}308static DEVICE_ATTR_RO(actual_brightness);309 310static ssize_t scale_show(struct device *dev,311		struct device_attribute *attr, char *buf)312{313	struct backlight_device *bd = to_backlight_device(dev);314 315	if (WARN_ON(bd->props.scale > BACKLIGHT_SCALE_NON_LINEAR))316		return sprintf(buf, "unknown\n");317 318	return sprintf(buf, "%s\n", backlight_scale_types[bd->props.scale]);319}320static DEVICE_ATTR_RO(scale);321 322#ifdef CONFIG_PM_SLEEP323static int backlight_suspend(struct device *dev)324{325	struct backlight_device *bd = to_backlight_device(dev);326 327	mutex_lock(&bd->ops_lock);328	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {329		bd->props.state |= BL_CORE_SUSPENDED;330		backlight_update_status(bd);331	}332	mutex_unlock(&bd->ops_lock);333 334	return 0;335}336 337static int backlight_resume(struct device *dev)338{339	struct backlight_device *bd = to_backlight_device(dev);340 341	mutex_lock(&bd->ops_lock);342	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {343		bd->props.state &= ~BL_CORE_SUSPENDED;344		backlight_update_status(bd);345	}346	mutex_unlock(&bd->ops_lock);347 348	return 0;349}350#endif351 352static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend,353			 backlight_resume);354 355static void bl_device_release(struct device *dev)356{357	struct backlight_device *bd = to_backlight_device(dev);358	kfree(bd);359}360 361static struct attribute *bl_device_attrs[] = {362	&dev_attr_bl_power.attr,363	&dev_attr_brightness.attr,364	&dev_attr_actual_brightness.attr,365	&dev_attr_max_brightness.attr,366	&dev_attr_scale.attr,367	&dev_attr_type.attr,368	NULL,369};370ATTRIBUTE_GROUPS(bl_device);371 372static const struct class backlight_class = {373	.name = "backlight",374	.dev_groups = bl_device_groups,375	.pm = &backlight_class_dev_pm_ops,376};377 378/**379 * backlight_force_update - tell the backlight subsystem that hardware state380 *   has changed381 * @bd: the backlight device to update382 * @reason: reason for update383 *384 * Updates the internal state of the backlight in response to a hardware event,385 * and generates an uevent to notify userspace. A backlight driver shall call386 * backlight_force_update() when the backlight is changed using, for example,387 * a hot-key. The updated brightness is read using get_brightness() and the388 * brightness value is reported using an uevent.389 */390void backlight_force_update(struct backlight_device *bd,391			    enum backlight_update_reason reason)392{393	int brightness;394 395	mutex_lock(&bd->ops_lock);396	if (bd->ops && bd->ops->get_brightness) {397		brightness = bd->ops->get_brightness(bd);398		if (brightness >= 0)399			bd->props.brightness = brightness;400		else401			dev_err(&bd->dev,402				"Could not update brightness from device: %pe\n",403				ERR_PTR(brightness));404	}405	mutex_unlock(&bd->ops_lock);406	backlight_generate_event(bd, reason);407}408EXPORT_SYMBOL(backlight_force_update);409 410/* deprecated - use devm_backlight_device_register() */411struct backlight_device *backlight_device_register(const char *name,412	struct device *parent, void *devdata, const struct backlight_ops *ops,413	const struct backlight_properties *props)414{415	struct backlight_device *new_bd;416	int rc;417 418	pr_debug("backlight_device_register: name=%s\n", name);419 420	new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);421	if (!new_bd)422		return ERR_PTR(-ENOMEM);423 424	mutex_init(&new_bd->update_lock);425	mutex_init(&new_bd->ops_lock);426 427	new_bd->dev.class = &backlight_class;428	new_bd->dev.parent = parent;429	new_bd->dev.release = bl_device_release;430	dev_set_name(&new_bd->dev, "%s", name);431	dev_set_drvdata(&new_bd->dev, devdata);432 433	/* Set default properties */434	if (props) {435		memcpy(&new_bd->props, props,436		       sizeof(struct backlight_properties));437		if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) {438			WARN(1, "%s: invalid backlight type", name);439			new_bd->props.type = BACKLIGHT_RAW;440		}441	} else {442		new_bd->props.type = BACKLIGHT_RAW;443	}444 445	rc = device_register(&new_bd->dev);446	if (rc) {447		put_device(&new_bd->dev);448		return ERR_PTR(rc);449	}450 451	rc = backlight_register_fb(new_bd);452	if (rc) {453		device_unregister(&new_bd->dev);454		return ERR_PTR(rc);455	}456 457	new_bd->ops = ops;458 459#ifdef CONFIG_PMAC_BACKLIGHT460	mutex_lock(&pmac_backlight_mutex);461	if (!pmac_backlight)462		pmac_backlight = new_bd;463	mutex_unlock(&pmac_backlight_mutex);464#endif465 466	mutex_lock(&backlight_dev_list_mutex);467	list_add(&new_bd->entry, &backlight_dev_list);468	mutex_unlock(&backlight_dev_list_mutex);469 470	blocking_notifier_call_chain(&backlight_notifier,471				     BACKLIGHT_REGISTERED, new_bd);472 473	return new_bd;474}475EXPORT_SYMBOL(backlight_device_register);476 477/** backlight_device_get_by_type - find first backlight device of a type478 * @type: the type of backlight device479 *480 * Look up the first backlight device of the specified type481 *482 * RETURNS:483 *484 * Pointer to backlight device if any was found. Otherwise NULL.485 */486struct backlight_device *backlight_device_get_by_type(enum backlight_type type)487{488	bool found = false;489	struct backlight_device *bd;490 491	mutex_lock(&backlight_dev_list_mutex);492	list_for_each_entry(bd, &backlight_dev_list, entry) {493		if (bd->props.type == type) {494			found = true;495			break;496		}497	}498	mutex_unlock(&backlight_dev_list_mutex);499 500	return found ? bd : NULL;501}502EXPORT_SYMBOL(backlight_device_get_by_type);503 504/**505 * backlight_device_get_by_name - Get backlight device by name506 * @name: Device name507 *508 * This function looks up a backlight device by its name. It obtains a reference509 * on the backlight device and it is the caller's responsibility to drop the510 * reference by calling put_device().511 *512 * Returns:513 * A pointer to the backlight device if found, otherwise NULL.514 */515struct backlight_device *backlight_device_get_by_name(const char *name)516{517	struct device *dev;518 519	dev = class_find_device_by_name(&backlight_class, name);520 521	return dev ? to_backlight_device(dev) : NULL;522}523EXPORT_SYMBOL(backlight_device_get_by_name);524 525/* deprecated - use devm_backlight_device_unregister() */526void backlight_device_unregister(struct backlight_device *bd)527{528	if (!bd)529		return;530 531	mutex_lock(&backlight_dev_list_mutex);532	list_del(&bd->entry);533	mutex_unlock(&backlight_dev_list_mutex);534 535#ifdef CONFIG_PMAC_BACKLIGHT536	mutex_lock(&pmac_backlight_mutex);537	if (pmac_backlight == bd)538		pmac_backlight = NULL;539	mutex_unlock(&pmac_backlight_mutex);540#endif541 542	blocking_notifier_call_chain(&backlight_notifier,543				     BACKLIGHT_UNREGISTERED, bd);544 545	mutex_lock(&bd->ops_lock);546	bd->ops = NULL;547	mutex_unlock(&bd->ops_lock);548 549	backlight_unregister_fb(bd);550	device_unregister(&bd->dev);551}552EXPORT_SYMBOL(backlight_device_unregister);553 554static void devm_backlight_device_release(struct device *dev, void *res)555{556	struct backlight_device *backlight = *(struct backlight_device **)res;557 558	backlight_device_unregister(backlight);559}560 561static int devm_backlight_device_match(struct device *dev, void *res,562					void *data)563{564	struct backlight_device **r = res;565 566	return *r == data;567}568 569/**570 * backlight_register_notifier - get notified of backlight (un)registration571 * @nb: notifier block with the notifier to call on backlight (un)registration572 *573 * Register a notifier to get notified when backlight devices get registered574 * or unregistered.575 *576 * RETURNS:577 *578 * 0 on success, otherwise a negative error code579 */580int backlight_register_notifier(struct notifier_block *nb)581{582	return blocking_notifier_chain_register(&backlight_notifier, nb);583}584EXPORT_SYMBOL(backlight_register_notifier);585 586/**587 * backlight_unregister_notifier - unregister a backlight notifier588 * @nb: notifier block to unregister589 *590 * Register a notifier to get notified when backlight devices get registered591 * or unregistered.592 *593 * RETURNS:594 *595 * 0 on success, otherwise a negative error code596 */597int backlight_unregister_notifier(struct notifier_block *nb)598{599	return blocking_notifier_chain_unregister(&backlight_notifier, nb);600}601EXPORT_SYMBOL(backlight_unregister_notifier);602 603/**604 * devm_backlight_device_register - register a new backlight device605 * @dev: the device to register606 * @name: the name of the device607 * @parent: a pointer to the parent device (often the same as @dev)608 * @devdata: an optional pointer to be stored for private driver use609 * @ops: the backlight operations structure610 * @props: the backlight properties611 *612 * Creates and registers new backlight device. When a backlight device613 * is registered the configuration must be specified in the @props614 * parameter. See description of &backlight_properties.615 *616 * RETURNS:617 *618 * struct backlight on success, or an ERR_PTR on error619 */620struct backlight_device *devm_backlight_device_register(struct device *dev,621	const char *name, struct device *parent, void *devdata,622	const struct backlight_ops *ops,623	const struct backlight_properties *props)624{625	struct backlight_device **ptr, *backlight;626 627	ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr),628			GFP_KERNEL);629	if (!ptr)630		return ERR_PTR(-ENOMEM);631 632	backlight = backlight_device_register(name, parent, devdata, ops,633						props);634	if (!IS_ERR(backlight)) {635		*ptr = backlight;636		devres_add(dev, ptr);637	} else {638		devres_free(ptr);639	}640 641	return backlight;642}643EXPORT_SYMBOL(devm_backlight_device_register);644 645/**646 * devm_backlight_device_unregister - unregister backlight device647 * @dev: the device to unregister648 * @bd: the backlight device to unregister649 *650 * Deallocates a backlight allocated with devm_backlight_device_register().651 * Normally this function will not need to be called and the resource management652 * code will ensure that the resources are freed.653 */654void devm_backlight_device_unregister(struct device *dev,655				struct backlight_device *bd)656{657	int rc;658 659	rc = devres_release(dev, devm_backlight_device_release,660				devm_backlight_device_match, bd);661	WARN_ON(rc);662}663EXPORT_SYMBOL(devm_backlight_device_unregister);664 665#ifdef CONFIG_OF666static int of_parent_match(struct device *dev, const void *data)667{668	return dev->parent && dev->parent->of_node == data;669}670 671/**672 * of_find_backlight_by_node() - find backlight device by device-tree node673 * @node: device-tree node of the backlight device674 *675 * Returns a pointer to the backlight device corresponding to the given DT676 * node or NULL if no such backlight device exists or if the device hasn't677 * been probed yet.678 *679 * This function obtains a reference on the backlight device and it is the680 * caller's responsibility to drop the reference by calling put_device() on681 * the backlight device's .dev field.682 */683struct backlight_device *of_find_backlight_by_node(struct device_node *node)684{685	struct device *dev;686 687	dev = class_find_device(&backlight_class, NULL, node, of_parent_match);688 689	return dev ? to_backlight_device(dev) : NULL;690}691EXPORT_SYMBOL(of_find_backlight_by_node);692#endif693 694static struct backlight_device *of_find_backlight(struct device *dev)695{696	struct backlight_device *bd = NULL;697	struct device_node *np;698 699	if (!dev)700		return NULL;701 702	if (IS_ENABLED(CONFIG_OF) && dev->of_node) {703		np = of_parse_phandle(dev->of_node, "backlight", 0);704		if (np) {705			bd = of_find_backlight_by_node(np);706			of_node_put(np);707			if (!bd)708				return ERR_PTR(-EPROBE_DEFER);709		}710	}711 712	return bd;713}714 715static void devm_backlight_release(void *data)716{717	struct backlight_device *bd = data;718 719	put_device(&bd->dev);720}721 722/**723 * devm_of_find_backlight - find backlight for a device724 * @dev: the device725 *726 * This function looks for a property named 'backlight' on the DT node727 * connected to @dev and looks up the backlight device. The lookup is728 * device managed so the reference to the backlight device is automatically729 * dropped on driver detach.730 *731 * RETURNS:732 *733 * A pointer to the backlight device if found.734 * Error pointer -EPROBE_DEFER if the DT property is set, but no backlight735 * device is found. NULL if there's no backlight property.736 */737struct backlight_device *devm_of_find_backlight(struct device *dev)738{739	struct backlight_device *bd;740	int ret;741 742	bd = of_find_backlight(dev);743	if (IS_ERR_OR_NULL(bd))744		return bd;745	ret = devm_add_action_or_reset(dev, devm_backlight_release, bd);746	if (ret)747		return ERR_PTR(ret);748 749	return bd;750}751EXPORT_SYMBOL(devm_of_find_backlight);752 753static void __exit backlight_class_exit(void)754{755	class_unregister(&backlight_class);756}757 758static int __init backlight_class_init(void)759{760	int ret;761 762	ret = class_register(&backlight_class);763	if (ret) {764		pr_warn("Unable to create backlight class; errno = %d\n", ret);765		return ret;766	}767 768	INIT_LIST_HEAD(&backlight_dev_list);769	mutex_init(&backlight_dev_list_mutex);770	BLOCKING_INIT_NOTIFIER_HEAD(&backlight_notifier);771 772	return 0;773}774 775/*776 * if this is compiled into the kernel, we need to ensure that the777 * class is registered before users of the class try to register lcd's778 */779postcore_initcall(backlight_class_init);780module_exit(backlight_class_exit);781 782MODULE_LICENSE("GPL");783MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");784MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");785