backlight.c 13.3 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
static const char *const backlight_types[] = {
M
Matthew Garrett 已提交
25 26 27 28 29
	[BACKLIGHT_RAW] = "raw",
	[BACKLIGHT_PLATFORM] = "platform",
	[BACKLIGHT_FIRMWARE] = "firmware",
};

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

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

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
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);
103
	sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
104 105
}

106
static ssize_t backlight_show_power(struct device *dev,
J
Jingoo Han 已提交
107
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
108
{
109
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
110

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

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

J
Jingoo Han 已提交
121
	rc = kstrtoul(buf, 0, &power);
122 123
	if (rc)
		return rc;
L
Linus Torvalds 已提交
124

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

	return rc;
}

140 141
static ssize_t backlight_show_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
142
{
143
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
144

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

148 149
static ssize_t backlight_store_brightness(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
L
Linus Torvalds 已提交
150
{
151
	int rc;
152
	struct backlight_device *bd = to_backlight_device(dev);
153
	unsigned long brightness;
L
Linus Torvalds 已提交
154

J
Jingoo Han 已提交
155
	rc = kstrtoul(buf, 0, &brightness);
156 157 158 159
	if (rc)
		return rc;

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

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

174 175
	backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);

L
Linus Torvalds 已提交
176 177 178
	return rc;
}

M
Matthew Garrett 已提交
179 180 181 182 183 184 185 186
static ssize_t backlight_show_type(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct backlight_device *bd = to_backlight_device(dev);

	return sprintf(buf, "%s\n", backlight_types[bd->props.type]);
}

187 188
static ssize_t backlight_show_max_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
189
{
190
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
191

192
	return sprintf(buf, "%d\n", bd->props.max_brightness);
193 194
}

195 196
static ssize_t backlight_show_actual_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
197 198
{
	int rc = -ENXIO;
199
	struct backlight_device *bd = to_backlight_device(dev);
200

201 202 203 204
	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 已提交
205 206 207 208

	return rc;
}

209
static struct class *backlight_class;
210

211 212
#ifdef CONFIG_PM_SLEEP
static int backlight_suspend(struct device *dev)
213 214 215
{
	struct backlight_device *bd = to_backlight_device(dev);

216 217
	mutex_lock(&bd->ops_lock);
	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
218 219 220
		bd->props.state |= BL_CORE_SUSPENDED;
		backlight_update_status(bd);
	}
221
	mutex_unlock(&bd->ops_lock);
222 223 224 225 226 227 228 229

	return 0;
}

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

230 231
	mutex_lock(&bd->ops_lock);
	if (bd->ops && bd->ops->options & BL_CORE_SUSPENDRESUME) {
232 233 234
		bd->props.state &= ~BL_CORE_SUSPENDED;
		backlight_update_status(bd);
	}
235
	mutex_unlock(&bd->ops_lock);
236 237 238

	return 0;
}
239 240 241 242
#endif

static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend,
			 backlight_resume);
243

244
static void bl_device_release(struct device *dev)
L
Linus Torvalds 已提交
245 246 247 248 249
{
	struct backlight_device *bd = to_backlight_device(dev);
	kfree(bd);
}

250 251 252
static struct device_attribute bl_device_attributes[] = {
	__ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
	__ATTR(brightness, 0644, backlight_show_brightness,
253
		     backlight_store_brightness),
254
	__ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
255
		     NULL),
256
	__ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
M
Matthew Garrett 已提交
257
	__ATTR(type, 0444, backlight_show_type, NULL),
258
	__ATTR_NULL,
L
Linus Torvalds 已提交
259 260
};

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
/**
 * 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 已提交
280 281 282 283 284
/**
 * 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).
285
 * @parent: a pointer to the parent device
286 287
 * @devdata: an optional pointer to be stored for private driver use. The
 *   methods may retrieve it by using bl_get_data(bd).
288
 * @ops: the backlight operations structure.
L
Linus Torvalds 已提交
289
 *
290
 * Creates and registers new backlight device. Returns either an
L
Linus Torvalds 已提交
291 292
 * ERR_PTR() or a pointer to the newly allocated device.
 */
293
struct backlight_device *backlight_device_register(const char *name,
294 295
	struct device *parent, void *devdata, const struct backlight_ops *ops,
	const struct backlight_properties *props)
L
Linus Torvalds 已提交
296 297
{
	struct backlight_device *new_bd;
298
	int rc;
L
Linus Torvalds 已提交
299

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

302
	new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
303
	if (!new_bd)
304
		return ERR_PTR(-ENOMEM);
L
Linus Torvalds 已提交
305

306
	mutex_init(&new_bd->update_lock);
307
	mutex_init(&new_bd->ops_lock);
L
Linus Torvalds 已提交
308

309 310 311
	new_bd->dev.class = backlight_class;
	new_bd->dev.parent = parent;
	new_bd->dev.release = bl_device_release;
312
	dev_set_name(&new_bd->dev, "%s", name);
313 314
	dev_set_drvdata(&new_bd->dev, devdata);

315
	/* Set default properties */
M
Matthew Garrett 已提交
316
	if (props) {
317 318
		memcpy(&new_bd->props, props,
		       sizeof(struct backlight_properties));
M
Matthew Garrett 已提交
319 320 321 322 323 324 325
		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;
	}
326

327
	rc = device_register(&new_bd->dev);
328
	if (rc) {
D
Dmitry Torokhov 已提交
329
		kfree(new_bd);
L
Linus Torvalds 已提交
330 331 332
		return ERR_PTR(rc);
	}

333
	rc = backlight_register_fb(new_bd);
D
Dmitry Torokhov 已提交
334
	if (rc) {
335
		device_unregister(&new_bd->dev);
D
Dmitry Torokhov 已提交
336 337 338
		return ERR_PTR(rc);
	}

339
	new_bd->ops = ops;
L
Linus Torvalds 已提交
340

341 342 343 344 345 346 347
#ifdef CONFIG_PMAC_BACKLIGHT
	mutex_lock(&pmac_backlight_mutex);
	if (!pmac_backlight)
		pmac_backlight = new_bd;
	mutex_unlock(&pmac_backlight_mutex);
#endif

L
Linus Torvalds 已提交
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	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)
{
	if (!bd)
		return;

363 364 365 366 367 368
#ifdef CONFIG_PMAC_BACKLIGHT
	mutex_lock(&pmac_backlight_mutex);
	if (pmac_backlight == bd)
		pmac_backlight = NULL;
	mutex_unlock(&pmac_backlight_mutex);
#endif
369 370 371
	mutex_lock(&bd->ops_lock);
	bd->ops = NULL;
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
372

373
	backlight_unregister_fb(bd);
374
	device_unregister(&bd->dev);
L
Linus Torvalds 已提交
375 376 377
}
EXPORT_SYMBOL(backlight_device_unregister);

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 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
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);

453
#ifdef CONFIG_OF
454
static int of_parent_match(struct device *dev, const void *data)
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
{
	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 已提交
482 483
static void __exit backlight_class_exit(void)
{
484
	class_destroy(backlight_class);
L
Linus Torvalds 已提交
485 486 487 488
}

static int __init backlight_class_init(void)
{
489 490
	backlight_class = class_create(THIS_MODULE, "backlight");
	if (IS_ERR(backlight_class)) {
491 492
		pr_warn("Unable to create backlight class; errno = %ld\n",
			PTR_ERR(backlight_class));
493 494 495 496
		return PTR_ERR(backlight_class);
	}

	backlight_class->dev_attrs = bl_device_attributes;
497
	backlight_class->pm = &backlight_class_dev_pm_ops;
498
	return 0;
L
Linus Torvalds 已提交
499 500 501 502 503 504 505 506 507 508 509 510
}

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