backlight.c 9.6 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
#ifdef CONFIG_PMAC_BACKLIGHT
#include <asm/backlight.h>
#endif
20 21 22 23 24 25 26 27 28 29 30 31 32 33

#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 ... */
34
	if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
35 36 37
		return 0;

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

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
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);
94
	sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness");
95 96
}

97 98
static ssize_t backlight_show_power(struct device *dev,
		struct device_attribute *attr,char *buf)
L
Linus Torvalds 已提交
99
{
100
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
101

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

105 106
static ssize_t backlight_store_power(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
L
Linus Torvalds 已提交
107
{
108
	int rc;
109
	struct backlight_device *bd = to_backlight_device(dev);
110
	unsigned long power;
L
Linus Torvalds 已提交
111

112 113 114
	rc = strict_strtoul(buf, 0, &power);
	if (rc)
		return rc;
L
Linus Torvalds 已提交
115

116
	rc = -ENXIO;
117 118
	mutex_lock(&bd->ops_lock);
	if (bd->ops) {
119
		pr_debug("backlight: set power to %lu\n", power);
120 121 122 123
		if (bd->props.power != power) {
			bd->props.power = power;
			backlight_update_status(bd);
		}
L
Linus Torvalds 已提交
124
		rc = count;
125
	}
126
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
127 128 129 130

	return rc;
}

131 132
static ssize_t backlight_show_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
133
{
134
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
135

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

139 140
static ssize_t backlight_store_brightness(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
L
Linus Torvalds 已提交
141
{
142
	int rc;
143
	struct backlight_device *bd = to_backlight_device(dev);
144
	unsigned long brightness;
L
Linus Torvalds 已提交
145

146 147 148 149 150
	rc = strict_strtoul(buf, 0, &brightness);
	if (rc)
		return rc;

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

152 153 154
	mutex_lock(&bd->ops_lock);
	if (bd->ops) {
		if (brightness > bd->props.max_brightness)
155 156
			rc = -EINVAL;
		else {
157
			pr_debug("backlight: set brightness to %lu\n",
158
				 brightness);
159 160
			bd->props.brightness = brightness;
			backlight_update_status(bd);
161 162 163
			rc = count;
		}
	}
164
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
165

166 167
	backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);

L
Linus Torvalds 已提交
168 169 170
	return rc;
}

171 172
static ssize_t backlight_show_max_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
173
{
174
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
175

176
	return sprintf(buf, "%d\n", bd->props.max_brightness);
177 178
}

179 180
static ssize_t backlight_show_actual_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
181 182
{
	int rc = -ENXIO;
183
	struct backlight_device *bd = to_backlight_device(dev);
184

185 186 187 188
	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 已提交
189 190 191 192

	return rc;
}

193
static struct class *backlight_class;
194

195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
static int backlight_suspend(struct device *dev, pm_message_t state)
{
	struct backlight_device *bd = to_backlight_device(dev);

	if (bd->ops->options & BL_CORE_SUSPENDRESUME) {
		mutex_lock(&bd->ops_lock);
		bd->props.state |= BL_CORE_SUSPENDED;
		backlight_update_status(bd);
		mutex_unlock(&bd->ops_lock);
	}

	return 0;
}

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

	if (bd->ops->options & BL_CORE_SUSPENDRESUME) {
		mutex_lock(&bd->ops_lock);
		bd->props.state &= ~BL_CORE_SUSPENDED;
		backlight_update_status(bd);
		mutex_unlock(&bd->ops_lock);
	}

	return 0;
}

223
static void bl_device_release(struct device *dev)
L
Linus Torvalds 已提交
224 225 226 227 228
{
	struct backlight_device *bd = to_backlight_device(dev);
	kfree(bd);
}

229 230 231
static struct device_attribute bl_device_attributes[] = {
	__ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
	__ATTR(brightness, 0644, backlight_show_brightness,
232
		     backlight_store_brightness),
233
	__ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
234
		     NULL),
235 236
	__ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
	__ATTR_NULL,
L
Linus Torvalds 已提交
237 238
};

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
/**
 * 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 已提交
258 259 260 261 262
/**
 * 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).
263
 * @parent: a pointer to the parent device
264 265
 * @devdata: an optional pointer to be stored for private driver use. The
 *   methods may retrieve it by using bl_get_data(bd).
266
 * @ops: the backlight operations structure.
L
Linus Torvalds 已提交
267
 *
268
 * Creates and registers new backlight device. Returns either an
L
Linus Torvalds 已提交
269 270
 * ERR_PTR() or a pointer to the newly allocated device.
 */
271
struct backlight_device *backlight_device_register(const char *name,
272 273
	struct device *parent, void *devdata, const struct backlight_ops *ops,
	const struct backlight_properties *props)
L
Linus Torvalds 已提交
274 275
{
	struct backlight_device *new_bd;
276
	int rc;
L
Linus Torvalds 已提交
277

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

280
	new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
281
	if (!new_bd)
282
		return ERR_PTR(-ENOMEM);
L
Linus Torvalds 已提交
283

284
	mutex_init(&new_bd->update_lock);
285
	mutex_init(&new_bd->ops_lock);
L
Linus Torvalds 已提交
286

287 288 289
	new_bd->dev.class = backlight_class;
	new_bd->dev.parent = parent;
	new_bd->dev.release = bl_device_release;
290
	dev_set_name(&new_bd->dev, name);
291 292
	dev_set_drvdata(&new_bd->dev, devdata);

293 294 295 296 297
	/* Set default properties */
	if (props)
		memcpy(&new_bd->props, props,
		       sizeof(struct backlight_properties));

298
	rc = device_register(&new_bd->dev);
299
	if (rc) {
D
Dmitry Torokhov 已提交
300
		kfree(new_bd);
L
Linus Torvalds 已提交
301 302 303
		return ERR_PTR(rc);
	}

304
	rc = backlight_register_fb(new_bd);
D
Dmitry Torokhov 已提交
305
	if (rc) {
306
		device_unregister(&new_bd->dev);
D
Dmitry Torokhov 已提交
307 308 309
		return ERR_PTR(rc);
	}

310
	new_bd->ops = ops;
L
Linus Torvalds 已提交
311

312 313 314 315 316 317 318
#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 已提交
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
	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;

334 335 336 337 338 339
#ifdef CONFIG_PMAC_BACKLIGHT
	mutex_lock(&pmac_backlight_mutex);
	if (pmac_backlight == bd)
		pmac_backlight = NULL;
	mutex_unlock(&pmac_backlight_mutex);
#endif
340 341 342
	mutex_lock(&bd->ops_lock);
	bd->ops = NULL;
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
343

344
	backlight_unregister_fb(bd);
345
	device_unregister(&bd->dev);
L
Linus Torvalds 已提交
346 347 348 349 350
}
EXPORT_SYMBOL(backlight_device_unregister);

static void __exit backlight_class_exit(void)
{
351
	class_destroy(backlight_class);
L
Linus Torvalds 已提交
352 353 354 355
}

static int __init backlight_class_init(void)
{
356 357 358 359 360 361 362 363
	backlight_class = class_create(THIS_MODULE, "backlight");
	if (IS_ERR(backlight_class)) {
		printk(KERN_WARNING "Unable to create backlight class; errno = %ld\n",
				PTR_ERR(backlight_class));
		return PTR_ERR(backlight_class);
	}

	backlight_class->dev_attrs = bl_device_attributes;
364 365
	backlight_class->suspend = backlight_suspend;
	backlight_class->resume = backlight_resume;
366
	return 0;
L
Linus Torvalds 已提交
367 368 369 370 371 372 373 374 375 376 377 378
}

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