11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Backlight Lowlevel Control Abstraction
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2003,2004 Hewlett-Packard Company
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/backlight.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/notifier.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ctype.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/err.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fb.h>
165a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie#ifdef CONFIG_PMAC_BACKLIGHT
19321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie#include <asm/backlight.h>
20321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie#endif
213d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
22c338bfb5ecf6c36b2112479691d69db4c2b5a78aBart Van Asschestatic const char *const backlight_types[] = {
23bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	[BACKLIGHT_RAW] = "raw",
24bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	[BACKLIGHT_PLATFORM] = "platform",
25bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	[BACKLIGHT_FIRMWARE] = "firmware",
26bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett};
27bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett
283d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
293d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons			   defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
303d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons/* This callback gets called when something important happens inside a
313d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons * framebuffer driver. We're looking if that important event is blanking,
323d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons * and if it is, we're switching backlight power as well ...
333d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons */
343d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmonsstatic int fb_notifier_callback(struct notifier_block *self,
353d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons				unsigned long event, void *data)
363d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons{
373d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	struct backlight_device *bd;
383d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	struct fb_event *evdata = data;
393d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
403d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	/* If we aren't interested in this event, skip it immediately ... */
41994efacdf9a087b52f71e620b58dfa526b0cf928Richard Purdie	if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
423d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons		return 0;
433d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
443d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	bd = container_of(self, struct backlight_device, fb_notif);
45599a52d12629394236d785615808845823875868Richard Purdie	mutex_lock(&bd->ops_lock);
46599a52d12629394236d785615808845823875868Richard Purdie	if (bd->ops)
47599a52d12629394236d785615808845823875868Richard Purdie		if (!bd->ops->check_fb ||
4857e148b6a975980944f4466ccb669b1d02dfc6a1Bruno Prémont		    bd->ops->check_fb(bd, evdata->info)) {
49599a52d12629394236d785615808845823875868Richard Purdie			bd->props.fb_blank = *(int *)evdata->data;
50c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie			if (bd->props.fb_blank == FB_BLANK_UNBLANK)
51c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie				bd->props.state &= ~BL_CORE_FBBLANK;
52c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie			else
53c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie				bd->props.state |= BL_CORE_FBBLANK;
5428ee086d5b36aab2931f6740e409bb0fb6c65e5fRichard Purdie			backlight_update_status(bd);
553d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons		}
56599a52d12629394236d785615808845823875868Richard Purdie	mutex_unlock(&bd->ops_lock);
573d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	return 0;
583d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons}
593d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
603d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmonsstatic int backlight_register_fb(struct backlight_device *bd)
613d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons{
623d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));
633d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	bd->fb_notif.notifier_call = fb_notifier_callback;
643d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
653d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	return fb_register_client(&bd->fb_notif);
663d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons}
673d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
683d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmonsstatic void backlight_unregister_fb(struct backlight_device *bd)
693d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons{
703d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	fb_unregister_client(&bd->fb_notif);
713d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons}
723d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons#else
733d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmonsstatic inline int backlight_register_fb(struct backlight_device *bd)
743d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons{
753d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	return 0;
763d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons}
773d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
783d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmonsstatic inline void backlight_unregister_fb(struct backlight_device *bd)
793d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons{
803d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons}
813d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons#endif /* CONFIG_FB */
823d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons
83325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrettstatic void backlight_generate_event(struct backlight_device *bd,
84325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett				     enum backlight_update_reason reason)
85325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett{
86325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	char *envp[2];
87325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett
88325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	switch (reason) {
89325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	case BACKLIGHT_UPDATE_SYSFS:
90325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett		envp[0] = "SOURCE=sysfs";
91325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett		break;
92325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	case BACKLIGHT_UPDATE_HOTKEY:
93325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett		envp[0] = "SOURCE=hotkey";
94325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett		break;
95325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	default:
96325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett		envp[0] = "SOURCE=unknown";
97325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett		break;
98325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	}
99325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	envp[1] = NULL;
100325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);
10189dfc28ccbff6521a4df9dc3699a94bbde11a6edHenrique de Moraes Holschuh	sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
102325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett}
103325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett
104655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic ssize_t backlight_show_power(struct device *dev,
10566655760bf38861299e3c8196f5303f886b0eef9Jingoo Han		struct device_attribute *attr, char *buf)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
107655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
109599a52d12629394236d785615808845823875868Richard Purdie	return sprintf(buf, "%d\n", bd->props.power);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
112655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic ssize_t backlight_store_power(struct device *dev,
113655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		struct device_attribute *attr, const char *buf, size_t count)
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1159a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	int rc;
116655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
1179a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	unsigned long power;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11966655760bf38861299e3c8196f5303f886b0eef9Jingoo Han	rc = kstrtoul(buf, 0, &power);
1209a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	if (rc)
1219a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek		return rc;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1239a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	rc = -ENXIO;
124599a52d12629394236d785615808845823875868Richard Purdie	mutex_lock(&bd->ops_lock);
125599a52d12629394236d785615808845823875868Richard Purdie	if (bd->ops) {
1269a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek		pr_debug("backlight: set power to %lu\n", power);
127515524537999c1f107a0a7c7f74c034979c2c86dHelge Deller		if (bd->props.power != power) {
128515524537999c1f107a0a7c7f74c034979c2c86dHelge Deller			bd->props.power = power;
129515524537999c1f107a0a7c7f74c034979c2c86dHelge Deller			backlight_update_status(bd);
130515524537999c1f107a0a7c7f74c034979c2c86dHelge Deller		}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = count;
1326ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie	}
133599a52d12629394236d785615808845823875868Richard Purdie	mutex_unlock(&bd->ops_lock);
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
138655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic ssize_t backlight_show_brightness(struct device *dev,
139655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		struct device_attribute *attr, char *buf)
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
141655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
143599a52d12629394236d785615808845823875868Richard Purdie	return sprintf(buf, "%d\n", bd->props.brightness);
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
146655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic ssize_t backlight_store_brightness(struct device *dev,
147655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		struct device_attribute *attr, const char *buf, size_t count)
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1499a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	int rc;
150655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
1519a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	unsigned long brightness;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15366655760bf38861299e3c8196f5303f886b0eef9Jingoo Han	rc = kstrtoul(buf, 0, &brightness);
1549a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	if (rc)
1559a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek		return rc;
1569a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek
1579a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek	rc = -ENXIO;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
159599a52d12629394236d785615808845823875868Richard Purdie	mutex_lock(&bd->ops_lock);
160599a52d12629394236d785615808845823875868Richard Purdie	if (bd->ops) {
161599a52d12629394236d785615808845823875868Richard Purdie		if (brightness > bd->props.max_brightness)
1626ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie			rc = -EINVAL;
1636ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie		else {
1649a2c61a921a8aeabacaccad0d2fdf75e1c1475fbPavel Machek			pr_debug("backlight: set brightness to %lu\n",
1656ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie				 brightness);
1669be1df98bca44dbe3769cd22f4ab8122b76c5313Zhang Rui			bd->props.brightness = brightness;
1679be1df98bca44dbe3769cd22f4ab8122b76c5313Zhang Rui			backlight_update_status(bd);
1686ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie			rc = count;
1696ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie		}
1706ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie	}
171599a52d12629394236d785615808845823875868Richard Purdie	mutex_unlock(&bd->ops_lock);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
173325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
174325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
178bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrettstatic ssize_t backlight_show_type(struct device *dev,
179bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett		struct device_attribute *attr, char *buf)
180bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett{
181bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	struct backlight_device *bd = to_backlight_device(dev);
182bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett
183bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	return sprintf(buf, "%s\n", backlight_types[bd->props.type]);
184bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett}
185bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett
186655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic ssize_t backlight_show_max_brightness(struct device *dev,
187655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		struct device_attribute *attr, char *buf)
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
189655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191599a52d12629394236d785615808845823875868Richard Purdie	return sprintf(buf, "%d\n", bd->props.max_brightness);
1926ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie}
1936ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie
194655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic ssize_t backlight_show_actual_brightness(struct device *dev,
195655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		struct device_attribute *attr, char *buf)
1966ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie{
1976ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie	int rc = -ENXIO;
198655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
1996ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie
200599a52d12629394236d785615808845823875868Richard Purdie	mutex_lock(&bd->ops_lock);
201599a52d12629394236d785615808845823875868Richard Purdie	if (bd->ops && bd->ops->get_brightness)
202599a52d12629394236d785615808845823875868Richard Purdie		rc = sprintf(buf, "%d\n", bd->ops->get_brightness(bd));
203599a52d12629394236d785615808845823875868Richard Purdie	mutex_unlock(&bd->ops_lock);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2080ad90efde278866a4ba7510e6e1fbd9626da1689Adrian Bunkstatic struct class *backlight_class;
209655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie
210c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdiestatic int backlight_suspend(struct device *dev, pm_message_t state)
211c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie{
212c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
213c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie
214d1d73578e053b981c3611e5a211534290d24a5ebUwe Kleine-König	mutex_lock(&bd->ops_lock);
215d1d73578e053b981c3611e5a211534290d24a5ebUwe Kleine-König	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
216c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie		bd->props.state |= BL_CORE_SUSPENDED;
217c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie		backlight_update_status(bd);
218c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	}
219d1d73578e053b981c3611e5a211534290d24a5ebUwe Kleine-König	mutex_unlock(&bd->ops_lock);
220c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie
221c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	return 0;
222c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie}
223c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie
224c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdiestatic int backlight_resume(struct device *dev)
225c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie{
226c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	struct backlight_device *bd = to_backlight_device(dev);
227c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie
228d1d73578e053b981c3611e5a211534290d24a5ebUwe Kleine-König	mutex_lock(&bd->ops_lock);
229d1d73578e053b981c3611e5a211534290d24a5ebUwe Kleine-König	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
230c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie		bd->props.state &= ~BL_CORE_SUSPENDED;
231c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie		backlight_update_status(bd);
232c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	}
233d1d73578e053b981c3611e5a211534290d24a5ebUwe Kleine-König	mutex_unlock(&bd->ops_lock);
234c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie
235c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	return 0;
236c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie}
237c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie
238655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic void bl_device_release(struct device *dev)
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct backlight_device *bd = to_backlight_device(dev);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(bd);
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
244655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdiestatic struct device_attribute bl_device_attributes[] = {
245655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	__ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
246655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	__ATTR(brightness, 0644, backlight_show_brightness,
2476ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie		     backlight_store_brightness),
248655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	__ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
2496ca017658b1f902c9bba2cc1017e301581f7728dRichard Purdie		     NULL),
250655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	__ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
251bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	__ATTR(type, 0444, backlight_show_type, NULL),
252655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	__ATTR_NULL,
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
256325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett * backlight_force_update - tell the backlight subsystem that hardware state
257325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett *   has changed
258325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett * @bd: the backlight device to update
259325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett *
260325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett * Updates the internal state of the backlight in response to a hardware event,
261325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett * and generate a uevent to notify userspace
262325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett */
263325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrettvoid backlight_force_update(struct backlight_device *bd,
264325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett			    enum backlight_update_reason reason)
265325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett{
266325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	mutex_lock(&bd->ops_lock);
267325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	if (bd->ops && bd->ops->get_brightness)
268325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett		bd->props.brightness = bd->ops->get_brightness(bd);
269325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	mutex_unlock(&bd->ops_lock);
270325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett	backlight_generate_event(bd, reason);
271325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett}
272325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew GarrettEXPORT_SYMBOL(backlight_force_update);
273325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett
274325253a6b2de4bdfa9ef0e28b5df8a4a4fe2b677Matthew Garrett/**
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * backlight_device_register - create and register a new object of
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   backlight_device class.
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @name: the name of the new object(must be the same as the name of the
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   respective framebuffer device).
279f6ec2d96796d0accda6c325890206f3629130729Sebastian Siewior * @parent: a pointer to the parent device
280655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie * @devdata: an optional pointer to be stored for private driver use. The
281655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie *   methods may retrieve it by using bl_get_data(bd).
282599a52d12629394236d785615808845823875868Richard Purdie * @ops: the backlight operations structure.
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
284655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie * Creates and registers new backlight device. Returns either an
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ERR_PTR() or a pointer to the newly allocated device.
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
287519ab5f2be65b72cf12ae99c89752bbe79b44df6Yu Lumingstruct backlight_device *backlight_device_register(const char *name,
288a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett	struct device *parent, void *devdata, const struct backlight_ops *ops,
289a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett	const struct backlight_properties *props)
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct backlight_device *new_bd;
292655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	int rc;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
294655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	pr_debug("backlight_device_register: name=%s\n", name);
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
296599a52d12629394236d785615808845823875868Richard Purdie	new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
29790968e8ebc4611896ff7f2ef0c0bf8455e845cd1Dmitry Torokhov	if (!new_bd)
29810ad1b7363090c0eec2b4054a5a3b82d2cc09ee5Jean Delvare		return ERR_PTR(-ENOMEM);
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30028ee086d5b36aab2931f6740e409bb0fb6c65e5fRichard Purdie	mutex_init(&new_bd->update_lock);
301599a52d12629394236d785615808845823875868Richard Purdie	mutex_init(&new_bd->ops_lock);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
303655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	new_bd->dev.class = backlight_class;
304655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	new_bd->dev.parent = parent;
305655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	new_bd->dev.release = bl_device_release;
30664dba9a9a33ec9ba00b0a504562d9f692ae89884Kay Sievers	dev_set_name(&new_bd->dev, name);
307655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	dev_set_drvdata(&new_bd->dev, devdata);
308655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie
309a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett	/* Set default properties */
310bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	if (props) {
311a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett		memcpy(&new_bd->props, props,
312a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett		       sizeof(struct backlight_properties));
313bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett		if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) {
314bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett			WARN(1, "%s: invalid backlight type", name);
315bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett			new_bd->props.type = BACKLIGHT_RAW;
316bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett		}
317bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	} else {
318bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett		new_bd->props.type = BACKLIGHT_RAW;
319bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett	}
320a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett
321655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	rc = device_register(&new_bd->dev);
32290968e8ebc4611896ff7f2ef0c0bf8455e845cd1Dmitry Torokhov	if (rc) {
3232fd5a15489dd2e58009ffd4ecbadb36a40f67d2bDmitry Torokhov		kfree(new_bd);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(rc);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3273d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	rc = backlight_register_fb(new_bd);
3282fd5a15489dd2e58009ffd4ecbadb36a40f67d2bDmitry Torokhov	if (rc) {
329655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		device_unregister(&new_bd->dev);
3302fd5a15489dd2e58009ffd4ecbadb36a40f67d2bDmitry Torokhov		return ERR_PTR(rc);
3312fd5a15489dd2e58009ffd4ecbadb36a40f67d2bDmitry Torokhov	}
3322fd5a15489dd2e58009ffd4ecbadb36a40f67d2bDmitry Torokhov
333655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	new_bd->ops = ops;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
335321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie#ifdef CONFIG_PMAC_BACKLIGHT
336321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie	mutex_lock(&pmac_backlight_mutex);
337321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie	if (!pmac_backlight)
338321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie		pmac_backlight = new_bd;
339321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie	mutex_unlock(&pmac_backlight_mutex);
340321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie#endif
341321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return new_bd;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(backlight_device_register);
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * backlight_device_unregister - unregisters a backlight device object.
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @bd: the backlight device object to be unregistered and freed.
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Unregisters a previously registered via backlight_device_register object.
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid backlight_device_unregister(struct backlight_device *bd)
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!bd)
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
357321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie#ifdef CONFIG_PMAC_BACKLIGHT
358321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie	mutex_lock(&pmac_backlight_mutex);
359321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie	if (pmac_backlight == bd)
360321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie		pmac_backlight = NULL;
361321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie	mutex_unlock(&pmac_backlight_mutex);
362321709c5994f952b78d567fd7083dbebbdc381b7Richard Purdie#endif
363599a52d12629394236d785615808845823875868Richard Purdie	mutex_lock(&bd->ops_lock);
364599a52d12629394236d785615808845823875868Richard Purdie	bd->ops = NULL;
365599a52d12629394236d785615808845823875868Richard Purdie	mutex_unlock(&bd->ops_lock);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3673d5eeaddad9338f39d25ee0c6c2ab1eda1ed2ef6James Simmons	backlight_unregister_fb(bd);
368655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	device_unregister(&bd->dev);
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(backlight_device_unregister);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit backlight_class_exit(void)
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
374655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	class_destroy(backlight_class);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init backlight_class_init(void)
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
379655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	backlight_class = class_create(THIS_MODULE, "backlight");
380655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	if (IS_ERR(backlight_class)) {
381655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
382655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie				PTR_ERR(backlight_class));
383655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie		return PTR_ERR(backlight_class);
384655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	}
385655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie
386655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	backlight_class->dev_attrs = bl_device_attributes;
387c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	backlight_class->suspend = backlight_suspend;
388c835ee7f4154992e6cf0674d7ee136f5d36247a4Richard Purdie	backlight_class->resume = backlight_resume;
389655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie	return 0;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * if this is compiled into the kernel, we need to ensure that the
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * class is registered before users of the class try to register lcd's
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldspostcore_initcall(backlight_class_init);
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(backlight_class_exit);
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
402