backlight.c 8.3 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 41 42
	mutex_lock(&bd->ops_lock);
	if (bd->ops)
		if (!bd->ops->check_fb ||
		    bd->ops->check_fb(evdata->info)) {
			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
static ssize_t backlight_show_power(struct device *dev,
		struct device_attribute *attr,char *buf)
L
Linus Torvalds 已提交
78
{
79
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
80

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

84 85
static ssize_t backlight_store_power(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
L
Linus Torvalds 已提交
86
{
87
	int rc;
88
	struct backlight_device *bd = to_backlight_device(dev);
89
	unsigned long power;
L
Linus Torvalds 已提交
90

91 92 93
	rc = strict_strtoul(buf, 0, &power);
	if (rc)
		return rc;
L
Linus Torvalds 已提交
94

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

	return rc;
}

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

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

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

125 126 127 128 129
	rc = strict_strtoul(buf, 0, &brightness);
	if (rc)
		return rc;

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

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

	return rc;
}

148 149
static ssize_t backlight_show_max_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
150
{
151
	struct backlight_device *bd = to_backlight_device(dev);
L
Linus Torvalds 已提交
152

153
	return sprintf(buf, "%d\n", bd->props.max_brightness);
154 155
}

156 157
static ssize_t backlight_show_actual_brightness(struct device *dev,
		struct device_attribute *attr, char *buf)
158 159
{
	int rc = -ENXIO;
160
	struct backlight_device *bd = to_backlight_device(dev);
161

162 163 164 165
	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 已提交
166 167 168 169

	return rc;
}

170
static struct class *backlight_class;
171

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
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;
}

200
static void bl_device_release(struct device *dev)
L
Linus Torvalds 已提交
201 202 203 204 205
{
	struct backlight_device *bd = to_backlight_device(dev);
	kfree(bd);
}

206 207 208
static struct device_attribute bl_device_attributes[] = {
	__ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
	__ATTR(brightness, 0644, backlight_show_brightness,
209
		     backlight_store_brightness),
210
	__ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
211
		     NULL),
212 213
	__ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
	__ATTR_NULL,
L
Linus Torvalds 已提交
214 215 216 217 218 219 220
};

/**
 * 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).
221
 * @parent: a pointer to the parent device
222 223
 * @devdata: an optional pointer to be stored for private driver use. The
 *   methods may retrieve it by using bl_get_data(bd).
224
 * @ops: the backlight operations structure.
L
Linus Torvalds 已提交
225
 *
226
 * Creates and registers new backlight device. Returns either an
L
Linus Torvalds 已提交
227 228
 * ERR_PTR() or a pointer to the newly allocated device.
 */
229
struct backlight_device *backlight_device_register(const char *name,
230
		struct device *parent, void *devdata, struct backlight_ops *ops)
L
Linus Torvalds 已提交
231 232
{
	struct backlight_device *new_bd;
233
	int rc;
L
Linus Torvalds 已提交
234

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

237
	new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL);
238
	if (!new_bd)
239
		return ERR_PTR(-ENOMEM);
L
Linus Torvalds 已提交
240

241
	mutex_init(&new_bd->update_lock);
242
	mutex_init(&new_bd->ops_lock);
L
Linus Torvalds 已提交
243

244 245 246
	new_bd->dev.class = backlight_class;
	new_bd->dev.parent = parent;
	new_bd->dev.release = bl_device_release;
247
	dev_set_name(&new_bd->dev, name);
248 249 250
	dev_set_drvdata(&new_bd->dev, devdata);

	rc = device_register(&new_bd->dev);
251
	if (rc) {
D
Dmitry Torokhov 已提交
252
		kfree(new_bd);
L
Linus Torvalds 已提交
253 254 255
		return ERR_PTR(rc);
	}

256
	rc = backlight_register_fb(new_bd);
D
Dmitry Torokhov 已提交
257
	if (rc) {
258
		device_unregister(&new_bd->dev);
D
Dmitry Torokhov 已提交
259 260 261
		return ERR_PTR(rc);
	}

262
	new_bd->ops = ops;
L
Linus Torvalds 已提交
263

264 265 266 267 268 269 270
#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 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	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;

286 287 288 289 290 291
#ifdef CONFIG_PMAC_BACKLIGHT
	mutex_lock(&pmac_backlight_mutex);
	if (pmac_backlight == bd)
		pmac_backlight = NULL;
	mutex_unlock(&pmac_backlight_mutex);
#endif
292 293 294
	mutex_lock(&bd->ops_lock);
	bd->ops = NULL;
	mutex_unlock(&bd->ops_lock);
L
Linus Torvalds 已提交
295

296
	backlight_unregister_fb(bd);
297
	device_unregister(&bd->dev);
L
Linus Torvalds 已提交
298 299 300 301 302
}
EXPORT_SYMBOL(backlight_device_unregister);

static void __exit backlight_class_exit(void)
{
303
	class_destroy(backlight_class);
L
Linus Torvalds 已提交
304 305 306 307
}

static int __init backlight_class_init(void)
{
308 309 310 311 312 313 314 315
	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;
316 317
	backlight_class->suspend = backlight_suspend;
	backlight_class->resume = backlight_resume;
318
	return 0;
L
Linus Torvalds 已提交
319 320 321 322 323 324 325 326 327 328 329 330
}

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