backlight.c 8.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Backlight Lowlevel Control Abstraction
 *
 * Copyright (C) 2003,2004 Hewlett-Packard Company
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/backlight.h>
#include <linux/notifier.h>
#include <linux/ctype.h>
#include <linux/err.h>
#include <linux/fb.h>

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

#if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \
			   defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE))
/* This callback gets called when something important happens inside a
 * framebuffer driver. We're looking if that important event is blanking,
 * and if it is, we're switching backlight power as well ...
 */
static int fb_notifier_callback(struct notifier_block *self,
				unsigned long event, void *data)
{
	struct backlight_device *bd;
	struct fb_event *evdata = data;

	/* If we aren't interested in this event, skip it immediately ... */
	if (event != FB_EVENT_BLANK)
		return 0;

	bd = container_of(self, struct backlight_device, fb_notif);
35
	mutex_lock(&bd->props_lock);
36 37 38 39
	if (bd->props)
		if (!bd->props->check_fb ||
		    bd->props->check_fb(evdata->info)) {
			bd->props->fb_blank = *(int *)evdata->data;
40
			backlight_update_status(bd);
41
		}
42
	mutex_unlock(&bd->props_lock);
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
	return 0;
}

static int backlight_register_fb(struct backlight_device *bd)
{
	memset(&bd->fb_notif, 0, sizeof(bd->fb_notif));
	bd->fb_notif.notifier_call = fb_notifier_callback;

	return fb_register_client(&bd->fb_notif);
}

static void backlight_unregister_fb(struct backlight_device *bd)
{
	fb_unregister_client(&bd->fb_notif);
}
#else
static inline int backlight_register_fb(struct backlight_device *bd)
{
	return 0;
}

static inline void backlight_unregister_fb(struct backlight_device *bd)
{
}
#endif /* CONFIG_FB */

L
Linus Torvalds 已提交
69 70
static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
{
71
	int rc = -ENXIO;
L
Linus Torvalds 已提交
72 73
	struct backlight_device *bd = to_backlight_device(cdev);

74
	mutex_lock(&bd->props_lock);
75
	if (bd->props)
76
		rc = sprintf(buf, "%d\n", bd->props->power);
77
	mutex_unlock(&bd->props_lock);
L
Linus Torvalds 已提交
78 79 80 81 82 83

	return rc;
}

static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count)
{
84
	int rc = -ENXIO;
L
Linus Torvalds 已提交
85 86
	char *endp;
	struct backlight_device *bd = to_backlight_device(cdev);
87 88
	int power = simple_strtoul(buf, &endp, 0);
	size_t size = endp - buf;
L
Linus Torvalds 已提交
89

90 91 92
	if (*endp && isspace(*endp))
		size++;
	if (size != count)
L
Linus Torvalds 已提交
93 94
		return -EINVAL;

95
	mutex_lock(&bd->props_lock);
96
	if (bd->props) {
L
Linus Torvalds 已提交
97
		pr_debug("backlight: set power to %d\n", power);
98
		bd->props->power = power;
99
		backlight_update_status(bd);
L
Linus Torvalds 已提交
100
		rc = count;
101
	}
102
	mutex_unlock(&bd->props_lock);
L
Linus Torvalds 已提交
103 104 105 106 107 108

	return rc;
}

static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
{
109
	int rc = -ENXIO;
L
Linus Torvalds 已提交
110 111
	struct backlight_device *bd = to_backlight_device(cdev);

112
	mutex_lock(&bd->props_lock);
113
	if (bd->props)
114
		rc = sprintf(buf, "%d\n", bd->props->brightness);
115
	mutex_unlock(&bd->props_lock);
L
Linus Torvalds 已提交
116 117 118 119 120 121

	return rc;
}

static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count)
{
122
	int rc = -ENXIO;
L
Linus Torvalds 已提交
123 124
	char *endp;
	struct backlight_device *bd = to_backlight_device(cdev);
125 126
	int brightness = simple_strtoul(buf, &endp, 0);
	size_t size = endp - buf;
L
Linus Torvalds 已提交
127

128 129 130
	if (*endp && isspace(*endp))
		size++;
	if (size != count)
L
Linus Torvalds 已提交
131 132
		return -EINVAL;

133
	mutex_lock(&bd->props_lock);
134
	if (bd->props) {
135 136 137 138 139 140
		if (brightness > bd->props->max_brightness)
			rc = -EINVAL;
		else {
			pr_debug("backlight: set brightness to %d\n",
				 brightness);
			bd->props->brightness = brightness;
141
			backlight_update_status(bd);
142 143 144
			rc = count;
		}
	}
145
	mutex_unlock(&bd->props_lock);
L
Linus Torvalds 已提交
146 147 148 149 150 151

	return rc;
}

static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf)
{
152
	int rc = -ENXIO;
L
Linus Torvalds 已提交
153 154
	struct backlight_device *bd = to_backlight_device(cdev);

155
	mutex_lock(&bd->props_lock);
156
	if (bd->props)
L
Linus Torvalds 已提交
157
		rc = sprintf(buf, "%d\n", bd->props->max_brightness);
158
	mutex_unlock(&bd->props_lock);
159 160 161 162 163 164 165 166 167 168

	return rc;
}

static ssize_t backlight_show_actual_brightness(struct class_device *cdev,
						char *buf)
{
	int rc = -ENXIO;
	struct backlight_device *bd = to_backlight_device(cdev);

169
	mutex_lock(&bd->props_lock);
170
	if (bd->props && bd->props->get_brightness)
171
		rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd));
172
	mutex_unlock(&bd->props_lock);
L
Linus Torvalds 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194

	return rc;
}

static void backlight_class_release(struct class_device *dev)
{
	struct backlight_device *bd = to_backlight_device(dev);
	kfree(bd);
}

static struct class backlight_class = {
	.name = "backlight",
	.release = backlight_class_release,
};

#define DECLARE_ATTR(_name,_mode,_show,_store)			\
{							 	\
	.attr	= { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE },	\
	.show	= _show,					\
	.store	= _store,					\
}

195
static const struct class_device_attribute bl_class_device_attributes[] = {
L
Linus Torvalds 已提交
196
	DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power),
197 198 199 200
	DECLARE_ATTR(brightness, 0644, backlight_show_brightness,
		     backlight_store_brightness),
	DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
		     NULL),
L
Linus Torvalds 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
};

/**
 * backlight_device_register - create and register a new object of
 *   backlight_device class.
 * @name: the name of the new object(must be the same as the name of the
 *   respective framebuffer device).
 * @devdata: an optional pointer to be stored in the class_device. The
 *   methods may retrieve it by using class_get_devdata(&bd->class_dev).
 * @bp: the backlight properties structure.
 *
 * Creates and registers new backlight class_device. Returns either an
 * ERR_PTR() or a pointer to the newly allocated device.
 */
216 217 218 219
struct backlight_device *backlight_device_register(const char *name,
	struct device *dev,
	void *devdata,
	struct backlight_properties *bp)
L
Linus Torvalds 已提交
220 221 222 223 224 225 226
{
	int i, rc;
	struct backlight_device *new_bd;

	pr_debug("backlight_device_alloc: name=%s\n", name);

	new_bd = kmalloc(sizeof(struct backlight_device), GFP_KERNEL);
227
	if (!new_bd)
228
		return ERR_PTR(-ENOMEM);
L
Linus Torvalds 已提交
229

230
	mutex_init(&new_bd->update_lock);
231
	mutex_init(&new_bd->props_lock);
L
Linus Torvalds 已提交
232 233 234
	new_bd->props = bp;
	memset(&new_bd->class_dev, 0, sizeof(new_bd->class_dev));
	new_bd->class_dev.class = &backlight_class;
235
	new_bd->class_dev.dev = dev;
L
Linus Torvalds 已提交
236 237 238 239
	strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN);
	class_set_devdata(&new_bd->class_dev, devdata);

	rc = class_device_register(&new_bd->class_dev);
240
	if (rc) {
D
Dmitry Torokhov 已提交
241
		kfree(new_bd);
L
Linus Torvalds 已提交
242 243 244
		return ERR_PTR(rc);
	}

245
	rc = backlight_register_fb(new_bd);
D
Dmitry Torokhov 已提交
246 247 248 249 250
	if (rc) {
		class_device_unregister(&new_bd->class_dev);
		return ERR_PTR(rc);
	}

L
Linus Torvalds 已提交
251 252 253 254

	for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) {
		rc = class_device_create_file(&new_bd->class_dev,
					      &bl_class_device_attributes[i]);
255
		if (rc) {
L
Linus Torvalds 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
			while (--i >= 0)
				class_device_remove_file(&new_bd->class_dev,
							 &bl_class_device_attributes[i]);
			class_device_unregister(&new_bd->class_dev);
			/* No need to kfree(new_bd) since release() method was called */
			return ERR_PTR(rc);
		}
	}

	return new_bd;
}
EXPORT_SYMBOL(backlight_device_register);

/**
 * backlight_device_unregister - unregisters a backlight device object.
 * @bd: the backlight device object to be unregistered and freed.
 *
 * Unregisters a previously registered via backlight_device_register object.
 */
void backlight_device_unregister(struct backlight_device *bd)
{
	int i;

	if (!bd)
		return;

	pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id);

	for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++)
		class_device_remove_file(&bd->class_dev,
					 &bl_class_device_attributes[i]);

288
	mutex_lock(&bd->props_lock);
L
Linus Torvalds 已提交
289
	bd->props = NULL;
290
	mutex_unlock(&bd->props_lock);
L
Linus Torvalds 已提交
291

292
	backlight_unregister_fb(bd);
L
Linus Torvalds 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

	class_device_unregister(&bd->class_dev);
}
EXPORT_SYMBOL(backlight_device_unregister);

static void __exit backlight_class_exit(void)
{
	class_unregister(&backlight_class);
}

static int __init backlight_class_init(void)
{
	return class_register(&backlight_class);
}

/*
 * if this is compiled into the kernel, we need to ensure that the
 * class is registered before users of the class try to register lcd's
 */
postcore_initcall(backlight_class_init);
module_exit(backlight_class_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");