backlight.c 14.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
/*
 * Backlight Lowlevel Control Abstraction
 *
 * Copyright (C) 2003,2004 Hewlett-Packard Company
 *
 */

8 9
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17
#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>
18
#include <linux/slab.h>
L
Linus Torvalds 已提交
19

20 21 22
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
23

24 25 26
static struct list_head backlight_dev_list;
static struct mutex backlight_dev_list_mutex;

27
static const char *const backlight_types[] = {
M
Matthew Garrett 已提交
28 29 30 31 32
	[BACKLIGHT_RAW] = "raw",
	[BACKLIGHT_PLATFORM] = "platform",
	[BACKLIGHT_FIRMWARE] = "firmware",
};

33 34 35 36 37 38 39 40 41 42 43 44 45
#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 ... */
46
	if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
47 48 49
		return 0;

	bd = container_of(self, struct backlight_device, fb_notif);
50 51 52
	mutex_lock(&bd->ops_lock);
	if (bd->ops)
		if (!bd->ops->check_fb ||
53
		    bd->ops->check_fb(bd, evdata->info)) {
54
			bd->props.fb_blank = *(int *)evdata->data;
55 56 57 58
			if (bd->props.fb_blank == FB_BLANK_UNBLANK)
				bd->props.state &= ~BL_CORE_FBBLANK;
			else
				bd->props.state |= BL_CORE_FBBLANK;
59
			backlight_update_status(bd);
60
		}
61
	mutex_unlock(&bd->ops_lock);
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	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 */

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
static void backlight_generate_event(struct backlight_device *bd,
				     enum backlight_update_reason reason)
{
	char *envp[2];

	switch (reason) {
	case BACKLIGHT_UPDATE_SYSFS:
		envp[0] = "SOURCE=sysfs";
		break;
	case BACKLIGHT_UPDATE_HOTKEY:
		envp[0] = "SOURCE=hotkey";
		break;
	default:
		envp[0] = "SOURCE=unknown";
		break;
	}
	envp[1] = NULL;
	kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp);
106
	sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
107 108
}

109 110
static ssize_t bl_power_show(struct device *dev, struct device_attribute *attr,
		char *buf)
L
Linus Torvalds 已提交
111
{
112
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
113

114
	return sprintf(buf, "%d\n", bd->props.power);
L
Linus Torvalds 已提交
115 116
}

117 118
static ssize_t bl_power_store(struct device *dev, struct device_attribute *attr,
		const char *buf, size_t count)
L
Linus Torvalds 已提交
119
{
120
	int rc;
121
	struct backlight_device *bd = to_backlight_device(dev);
122
	unsigned long power;
L
Linus Torvalds 已提交
123

J
Jingoo Han 已提交
124
	rc = kstrtoul(buf, 0, &power);
125 126
	if (rc)
		return rc;
L
Linus Torvalds 已提交
127

128
	rc = -ENXIO;
129 130
	mutex_lock(&bd->ops_lock);
	if (bd->ops) {
131
		pr_debug("set power to %lu\n", power);
132 133 134 135
		if (bd->props.power != power) {
			bd->props.power = power;
			backlight_update_status(bd);
		}
L
Linus Torvalds 已提交
136
		rc = count;
137
	}
138
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
139 140 141

	return rc;
}
142
static DEVICE_ATTR_RW(bl_power);
L
Linus Torvalds 已提交
143

144
static ssize_t brightness_show(struct device *dev,
145
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
146
{
147
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
148

149
	return sprintf(buf, "%d\n", bd->props.brightness);
L
Linus Torvalds 已提交
150 151
}

152
static ssize_t brightness_store(struct device *dev,
153
		struct device_attribute *attr, const char *buf, size_t count)
L
Linus Torvalds 已提交
154
{
155
	int rc;
156
	struct backlight_device *bd = to_backlight_device(dev);
157
	unsigned long brightness;
L
Linus Torvalds 已提交
158

J
Jingoo Han 已提交
159
	rc = kstrtoul(buf, 0, &brightness);
160 161 162 163
	if (rc)
		return rc;

	rc = -ENXIO;
L
Linus Torvalds 已提交
164

165 166 167
	mutex_lock(&bd->ops_lock);
	if (bd->ops) {
		if (brightness > bd->props.max_brightness)
168 169
			rc = -EINVAL;
		else {
170
			pr_debug("set brightness to %lu\n", brightness);
171 172
			bd->props.brightness = brightness;
			backlight_update_status(bd);
173 174 175
			rc = count;
		}
	}
176
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
177

178 179
	backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);

L
Linus Torvalds 已提交
180 181
	return rc;
}
182
static DEVICE_ATTR_RW(brightness);
L
Linus Torvalds 已提交
183

184 185
static ssize_t type_show(struct device *dev, struct device_attribute *attr,
		char *buf)
M
Matthew Garrett 已提交
186 187 188 189 190
{
	struct backlight_device *bd = to_backlight_device(dev);

	return sprintf(buf, "%s\n", backlight_types[bd->props.type]);
}
191
static DEVICE_ATTR_RO(type);
M
Matthew Garrett 已提交
192

193
static ssize_t max_brightness_show(struct device *dev,
194
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
195
{
196
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
197

198
	return sprintf(buf, "%d\n", bd->props.max_brightness);
199
}
200
static DEVICE_ATTR_RO(max_brightness);
201

202
static ssize_t actual_brightness_show(struct device *dev,
203
		struct device_attribute *attr, char *buf)
204 205
{
	int rc = -ENXIO;
206
	struct backlight_device *bd = to_backlight_device(dev);
207

208 209 210 211
	mutex_lock(&bd->ops_lock);
	if (bd->ops && bd->ops->get_brightness)
		rc = sprintf(buf, "%d\n", bd->ops->get_brightness(bd));
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
212 213 214

	return rc;
}
215
static DEVICE_ATTR_RO(actual_brightness);
L
Linus Torvalds 已提交
216

217
static struct class *backlight_class;
218

219 220
#ifdef CONFIG_PM_SLEEP
static int backlight_suspend(struct device *dev)
221 222 223
{
	struct backlight_device *bd = to_backlight_device(dev);

224 225
	mutex_lock(&bd->ops_lock);
	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
226 227 228
		bd->props.state |= BL_CORE_SUSPENDED;
		backlight_update_status(bd);
	}
229
	mutex_unlock(&bd->ops_lock);
230 231 232 233 234 235 236 237

	return 0;
}

static int backlight_resume(struct device *dev)
{
	struct backlight_device *bd = to_backlight_device(dev);

238 239
	mutex_lock(&bd->ops_lock);
	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
240 241 242
		bd->props.state &= ~BL_CORE_SUSPENDED;
		backlight_update_status(bd);
	}
243
	mutex_unlock(&bd->ops_lock);
244 245 246

	return 0;
}
247 248 249 250
#endif

static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend,
			 backlight_resume);
251

252
static void bl_device_release(struct device *dev)
L
Linus Torvalds 已提交
253 254 255 256 257
{
	struct backlight_device *bd = to_backlight_device(dev);
	kfree(bd);
}

258 259 260 261 262 263 264
static struct attribute *bl_device_attrs[] = {
	&dev_attr_bl_power.attr,
	&dev_attr_brightness.attr,
	&dev_attr_actual_brightness.attr,
	&dev_attr_max_brightness.attr,
	&dev_attr_type.attr,
	NULL,
L
Linus Torvalds 已提交
265
};
266
ATTRIBUTE_GROUPS(bl_device);
L
Linus Torvalds 已提交
267

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
/**
 * backlight_force_update - tell the backlight subsystem that hardware state
 *   has changed
 * @bd: the backlight device to update
 *
 * Updates the internal state of the backlight in response to a hardware event,
 * and generate a uevent to notify userspace
 */
void backlight_force_update(struct backlight_device *bd,
			    enum backlight_update_reason reason)
{
	mutex_lock(&bd->ops_lock);
	if (bd->ops && bd->ops->get_brightness)
		bd->props.brightness = bd->ops->get_brightness(bd);
	mutex_unlock(&bd->ops_lock);
	backlight_generate_event(bd, reason);
}
EXPORT_SYMBOL(backlight_force_update);

L
Linus Torvalds 已提交
287 288 289 290 291
/**
 * 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).
292
 * @parent: a pointer to the parent device
293 294
 * @devdata: an optional pointer to be stored for private driver use. The
 *   methods may retrieve it by using bl_get_data(bd).
295
 * @ops: the backlight operations structure.
L
Linus Torvalds 已提交
296
 *
297
 * Creates and registers new backlight device. Returns either an
L
Linus Torvalds 已提交
298 299
 * ERR_PTR() or a pointer to the newly allocated device.
 */
300
struct backlight_device *backlight_device_register(const char *name,
301 302
	struct device *parent, void *devdata, const struct backlight_ops *ops,
	const struct backlight_properties *props)
L
Linus Torvalds 已提交
303 304
{
	struct backlight_device *new_bd;
305
	int rc;
L
Linus Torvalds 已提交
306

307
	pr_debug("backlight_device_register: name=%s\n", name);
L
Linus Torvalds 已提交
308

309
	new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
310
	if (!new_bd)
311
		return ERR_PTR(-ENOMEM);
L
Linus Torvalds 已提交
312

313
	mutex_init(&new_bd->update_lock);
314
	mutex_init(&new_bd->ops_lock);
L
Linus Torvalds 已提交
315

316 317 318
	new_bd->dev.class = backlight_class;
	new_bd->dev.parent = parent;
	new_bd->dev.release = bl_device_release;
319
	dev_set_name(&new_bd->dev, "%s", name);
320 321
	dev_set_drvdata(&new_bd->dev, devdata);

322
	/* Set default properties */
M
Matthew Garrett 已提交
323
	if (props) {
324 325
		memcpy(&new_bd->props, props,
		       sizeof(struct backlight_properties));
M
Matthew Garrett 已提交
326 327 328 329 330 331 332
		if (props->type <= 0 || props->type >= BACKLIGHT_TYPE_MAX) {
			WARN(1, "%s: invalid backlight type", name);
			new_bd->props.type = BACKLIGHT_RAW;
		}
	} else {
		new_bd->props.type = BACKLIGHT_RAW;
	}
333

334
	rc = device_register(&new_bd->dev);
335
	if (rc) {
336
		put_device(&new_bd->dev);
L
Linus Torvalds 已提交
337 338 339
		return ERR_PTR(rc);
	}

340
	rc = backlight_register_fb(new_bd);
D
Dmitry Torokhov 已提交
341
	if (rc) {
342
		device_unregister(&new_bd->dev);
D
Dmitry Torokhov 已提交
343 344 345
		return ERR_PTR(rc);
	}

346
	new_bd->ops = ops;
L
Linus Torvalds 已提交
347

348 349 350 351 352 353 354
#ifdef CONFIG_PMAC_BACKLIGHT
	mutex_lock(&pmac_backlight_mutex);
	if (!pmac_backlight)
		pmac_backlight = new_bd;
	mutex_unlock(&pmac_backlight_mutex);
#endif

355 356 357 358
	mutex_lock(&backlight_dev_list_mutex);
	list_add(&new_bd->entry, &backlight_dev_list);
	mutex_unlock(&backlight_dev_list_mutex);

L
Linus Torvalds 已提交
359 360 361 362
	return new_bd;
}
EXPORT_SYMBOL(backlight_device_register);

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
bool backlight_device_registered(enum backlight_type type)
{
	bool found = false;
	struct backlight_device *bd;

	mutex_lock(&backlight_dev_list_mutex);
	list_for_each_entry(bd, &backlight_dev_list, entry) {
		if (bd->props.type == type) {
			found = true;
			break;
		}
	}
	mutex_unlock(&backlight_dev_list_mutex);

	return found;
}
EXPORT_SYMBOL(backlight_device_registered);

L
Linus Torvalds 已提交
381 382 383 384 385 386 387 388 389 390 391
/**
 * 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)
{
	if (!bd)
		return;

392 393 394 395
	mutex_lock(&backlight_dev_list_mutex);
	list_del(&bd->entry);
	mutex_unlock(&backlight_dev_list_mutex);

396 397 398 399 400 401
#ifdef CONFIG_PMAC_BACKLIGHT
	mutex_lock(&pmac_backlight_mutex);
	if (pmac_backlight == bd)
		pmac_backlight = NULL;
	mutex_unlock(&pmac_backlight_mutex);
#endif
402 403 404
	mutex_lock(&bd->ops_lock);
	bd->ops = NULL;
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
405

406
	backlight_unregister_fb(bd);
407
	device_unregister(&bd->dev);
L
Linus Torvalds 已提交
408 409 410
}
EXPORT_SYMBOL(backlight_device_unregister);

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
static void devm_backlight_device_release(struct device *dev, void *res)
{
	struct backlight_device *backlight = *(struct backlight_device **)res;

	backlight_device_unregister(backlight);
}

static int devm_backlight_device_match(struct device *dev, void *res,
					void *data)
{
	struct backlight_device **r = res;

	return *r == data;
}

/**
 * devm_backlight_device_register - resource managed backlight_device_register()
 * @dev: the device to register
 * @name: the name of the device
 * @parent: a pointer to the parent device
 * @devdata: an optional pointer to be stored for private driver use
 * @ops: the backlight operations structure
 * @props: the backlight properties
 *
 * @return a struct backlight on success, or an ERR_PTR on error
 *
 * Managed backlight_device_register(). The backlight_device returned
 * from this function are automatically freed on driver detach.
 * See backlight_device_register() for more information.
 */
struct backlight_device *devm_backlight_device_register(struct device *dev,
	const char *name, struct device *parent, void *devdata,
	const struct backlight_ops *ops,
	const struct backlight_properties *props)
{
	struct backlight_device **ptr, *backlight;

	ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr),
			GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	backlight = backlight_device_register(name, parent, devdata, ops,
						props);
	if (!IS_ERR(backlight)) {
		*ptr = backlight;
		devres_add(dev, ptr);
	} else {
		devres_free(ptr);
	}

	return backlight;
}
EXPORT_SYMBOL(devm_backlight_device_register);

/**
 * devm_backlight_device_unregister - resource managed backlight_device_unregister()
 * @dev: the device to unregister
 * @bd: the backlight device to unregister
 *
 * Deallocated a backlight allocated with devm_backlight_device_register().
 * Normally this function will not need to be called and the resource management
 * code will ensure that the resource is freed.
 */
void devm_backlight_device_unregister(struct device *dev,
				struct backlight_device *bd)
{
	int rc;

	rc = devres_release(dev, devm_backlight_device_release,
				devm_backlight_device_match, bd);
	WARN_ON(rc);
}
EXPORT_SYMBOL(devm_backlight_device_unregister);

486
#ifdef CONFIG_OF
487
static int of_parent_match(struct device *dev, const void *data)
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
{
	return dev->parent && dev->parent->of_node == data;
}

/**
 * of_find_backlight_by_node() - find backlight device by device-tree node
 * @node: device-tree node of the backlight device
 *
 * Returns a pointer to the backlight device corresponding to the given DT
 * node or NULL if no such backlight device exists or if the device hasn't
 * been probed yet.
 *
 * This function obtains a reference on the backlight device and it is the
 * caller's responsibility to drop the reference by calling put_device() on
 * the backlight device's .dev field.
 */
struct backlight_device *of_find_backlight_by_node(struct device_node *node)
{
	struct device *dev;

	dev = class_find_device(backlight_class, NULL, node, of_parent_match);

	return dev ? to_backlight_device(dev) : NULL;
}
EXPORT_SYMBOL(of_find_backlight_by_node);
#endif

L
Linus Torvalds 已提交
515 516
static void __exit backlight_class_exit(void)
{
517
	class_destroy(backlight_class);
L
Linus Torvalds 已提交
518 519 520 521
}

static int __init backlight_class_init(void)
{
522 523
	backlight_class = class_create(THIS_MODULE, "backlight");
	if (IS_ERR(backlight_class)) {
524 525
		pr_warn("Unable to create backlight class; errno = %ld\n",
			PTR_ERR(backlight_class));
526 527 528
		return PTR_ERR(backlight_class);
	}

529
	backlight_class->dev_groups = bl_device_groups;
530
	backlight_class->pm = &backlight_class_dev_pm_ops;
531 532
	INIT_LIST_HEAD(&backlight_dev_list);
	mutex_init(&backlight_dev_list_mutex);
533
	return 0;
L
Linus Torvalds 已提交
534 535 536 537 538 539 540 541 542 543 544 545
}

/*
 * 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");